C# --- IEnumerable 和 IEnumerator
- IEnumerable
- IEnumerator
- IEnumerable 和 IEnumerator 的作用
- 手动实现 IEnumerable
- IEnumerable vs. IQueryable
- 为什么有了ienumerator还需要ienumerable
IEnumerable
- 在C#中,
IEnumerable
是一个核心接口,用于表示一个可迭代的集合。它是实现迭代模式的基础,允许你通过 foreach 循环遍历集合中的元素- IEnumerable 是一个接口,属于 System.Collections 命名空间。它的泛型版本 IEnumerable 位于 System.Collections.Generic 中。
- 作用:标记一个对象可以被迭代(即支持 foreach 循环)。
- 核心方法:
GetEnumerator()
,返回一个IEnumerator
对象,用于逐个访问集合中的元素。
public interface IEnumerable
{IEnumerator GetEnumerator();
}// 泛型版本(更常用):
public interface IEnumerable<out T> : IEnumerable
{IEnumerator<T> GetEnumerator();
}
IEnumerator
- IEnumerator 是实际执行迭代的接口,相当于Java里的
iterator
, 他定义了三个关键方法:
- MoveNext():移动到集合的下一个元素,返回 bool 表示是否成功。
- Current:获取当前元素的值。
- Reset():重置迭代器到初始位置(通常不常用)
public interface IEnumerator
{bool MoveNext();object Current { get; }void Reset();
}// 泛型版本:
public interface IEnumerator<out T> : IEnumerator, IDisposable
{T Current { get; }
}
IEnumerable 和 IEnumerator 的作用
- 统一迭代方式:任何实现 IEnumerable 的类型都可以用
foreach
遍历。- 支持 LINQ:LINQ 的查询操作(如 Where、Select)基于 IEnumerable,实现延迟执行。
- 抽象集合类型:允许代码操作抽象的集合(
IEnumerable<T>
),而非具体类型(如 List),提高灵活性。
手动实现 IEnumerable
using System;
using System.Collections;public class MyCollection : IEnumerable
{private int[] _data = { 1, 2, 3 };public IEnumerator GetEnumerator(){return new MyEnumerator(_data);}
}public class MyEnumerator : IEnumerator
{private int[] _data;private int _index = -1;public MyEnumerator(int[] data){_data = data;}public object Current => _data[_index];public bool MoveNext(){_index++;return _index < _data.Length;}public void Reset(){_index = -1;}
}// 使用:
var collection = new MyCollection();
foreach (var num in collection)
{Console.WriteLine(num); // 输出 1, 2, 3
}
IEnumerable vs. IQueryable
- IEnumerable:
用于内存中集合(如 List、数组)。
LINQ 操作由 C# 编译器处理(如 Where 转换为委托)。
- IQueryable:
用于外部数据源(如数据库)。
LINQ 操作转换为表达式树(如 SQL 查询),由提供程序解析
为什么有了ienumerator还需要ienumerable
IEnumerable:
- 角色:表示一个集合是“可迭代的”。
核心功能:通过 GetEnumerator() 方法返回一个迭代器(IEnumerator 对象)。- 意义:提供统一的接口,让所有集合类型(如数组、List、自定义集合)都能被 foreach 遍历。
IEnumerator:
- 角色:实际控制迭代过程(移动指针、获取当前元素)。
- 核心功能:通过 MoveNext() 和 Current 实现逐个元素的遍历。
- 意义:封装迭代状态(如当前索引),
确保多次遍历互不干扰
。
场景假设
- 假设一个集合类直接实现 IEnumerator, 如果两个 foreach 同时遍历同一个 BadList 实例,它们的 _index 会互相干扰:
无法支持并发迭代:同一实例的迭代状态会被覆盖
public class BadList : IEnumerator<int>
{private int[] _data = { 1, 2, 3 };private int _index = -1;public int Current => _data[_index];object IEnumerator.Current => Current;public bool MoveNext(){_index++;return _index < _data.Length;}public void Reset() => _index = -1;public void Dispose() { }
}
var badList = new BadList();
foreach (var num in badList) { /* 第一次遍历 */ } // 输出 1,2,3
foreach (var num in badList) { /* 第二次遍历 */ } // 无输出(索引已到末尾)
正确做法:IEnumerable + IEnumerator
public class GoodList : IEnumerable<int>
{private int[] _data = { 1, 2, 3 };public IEnumerator<int> GetEnumerator(){// 每次调用返回一个新的迭代器return new GoodListEnumerator(_data);}// 显式实现非泛型接口IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}public class GoodListEnumerator : IEnumerator<int>
{private int[] _data;private int _index = -1;public GoodListEnumerator(int[] data) => _data = data;public int Current => _data[_index];object IEnumerator.Current => Current;public bool MoveNext(){_index++;return _index < _data.Length;}public void Reset() => _index = -1;public void Dispose() { }
}
- 状态隔离:
每次 foreach 会调用 GetEnumerator() 生成新的 IEnumerator 实例,确保每次遍历独立。
- 并发安全:多个遍历操作互不影响。
- 简化代码:foreach 自动管理迭代器生命周期(调用 Dispose())。