foreach 迭代器的实现
foreach 迭代器可以遍历所有实现了IEnumerable接口或者提供了 IEnumerable实现的类。
MSDN解释:
在 C# 中,集合类并非必须严格从 IEnumerable 和 IEnumerator 继承才能与 foreach 兼容;只要类有所需的 GetEnumerator、MoveNext、Reset 和 Current 成员,便可以与 foreach 一起使用。省略接口的好处为,使您可以将 Current 的返回类型定义得比 object 更明确,从而提供了类型安全。
查看IEnumerable的实现,只是简单的返回了一个IEnumerator 对象,所有的处理都被委托到了IEnumerator。
public interface IEnumerable { IEnumerator GetEnumerator(); } public interface IEnumerator { object Current { get; }//获取集合中的当前元素 bool MoveNext();//将枚举数推进到集合的下一个元素 void Reset();//将枚举数设置为其初始位置,该位置位于集合中第一个元素之前 }
简单示例
class MyEnumerable:IEnumerable { int[] values; public MyEnumerable(int[] values) { this.values = values; } #region IEnumerable 成员 public IEnumerator GetEnumerator() { return new MyEnumerator(this); } #endregion class MyEnumerator : IEnumerator { MyEnumerable parent; int position = -1; public MyEnumerator(MyEnumerable parent) { this.parent = parent; } #region IEnumerator 成员 public object Current { get { return parent.values[position]; } } public bool MoveNext() { return ++position < parent.values.Length; } public void Reset() { position = -1; } #endregion } }
由于 IEnumerator接口定义的 Current类型为 Object,所以在使用foreach循环中,获取到的元素类型也是objec。
static void Main(string[] args) { int[] values = { 1, 2, 3 }; MyEnumerable collection = new MyEnumerable(values); foreach (var value in collection) { int temp = value + 1;//编译出错,无法将objec和int类型相加 Console.WriteLine(temp); } Console.Read(); }
可以显示指定foreach中的元素类型 比如 foreach (int value in collection) ,在每次遍历的时候 执行一次强制转换,如果是值类型,还会涉及到装箱和拆箱。
所以我们使用IEnumerable<T>替代IEnumerable
class MyEnumerable : IEnumerable<int> { int[] values; public MyEnumerable(int[] values) { this.values = values; } public IEnumerator<int> GetEnumerator() { return new MyEnumerator(this); } class MyEnumerator : IEnumerator<int> { public int Current { get { return parent.values[position]; } }
……………… } }
最主要的区别就是 Current 的类型为定义的泛型类型,此处为int。
迭代器的实现过于繁琐, 可以使用 yield return 替代繁琐的IEnumerator内嵌类
public IEnumerator<int> GetEnumerator() { for (int i = 0; i < values.Length; i++) yield return values[i]; }
yield return 内部实现了一个状态机,具体就不展开了~~