枚举器
实现枚举器(enumerator)
的对象称为可枚举类型(enumerable type 或enumerable)。
调用可枚举类型的GetEnumerator方法获取枚举器。
foreach原理:
①调用GetEnumerator方法获取enumerable的枚举器。
②从枚举器请求每一项并把它作为迭代变量,代码可以读取变量但是不能改变。
IEnumerator接口和IEnumerable接口
实现IEnumerator接口的枚举器包含三个函数成员:
- Current
只读属性,返回object类型的引用。 - MoveNext
把枚举器位置前进到集合的下一项。返回布尔值,只是新的位置是否有效,若有效返回true。
枚举器的原始位置处于序列中的第一项前,所以MoveNext必须在第一次使用Current前调用。 - Reset
把位置重置为原始状态。
IEnumerable接口只有一个成员:GetEnumerator方法,返回对象的枚举器。
实现了IEnumerable接口的类称为可枚举类。
自定义一个可枚举类的方法
- 自定义类MyEnumerator继承
IEnumerator
接口,作为枚举器
实现Current,MoveNext、Reset方法。 - 自定义类MyEnumerable继承
IEnumerable
接口
实现GetEnumerator方法,获取枚举器(MyEnumerator)。
实际上,大多数情况下应该使用IEnumerable<T>
和IEnumerator<T>
。
IEnumerable< T>的GetEnumerator()返回实现IEnumerator< T>的枚举器类的实例。IEnumerator< T>的Current()返回具体类型而不是object。
非泛型接口的实现不是类型安全的(返回object需要转化为实际类型),而泛型接口的实现是类型安全的,它返回具体类型。
迭代器
在System.Collections.Generic
命名空间下,使用时需要using
C#从2.0版本开始提供迭代器。
yield return
指定了序列中返回的下一项yield break
指定在序列中没有其他项
class MyClass
{
public IEnumerator<string> GetEnumerator() { return BlackAndWhite();}
//迭代器,返回一个枚举器,foreach操作MyClass类的对象会依次获得下面三个字符串
public IEnumerator<string> BlackAndWhite()
{
yield return "black";
yield return "gray";
yield return "white";
}
//迭代器,返回一个可枚举类型,,foreach(string s in mc.AToB())依次获得两个字符串
public IEnumerable<string> TBlackAndWhite()
{
yield return "black";
yield return "gray";
yield return "white";
}
}
编译器生成的枚举器并不实现Reset()方法,调用时会抛出System.NotSupportedException
异常。
在后台,由编译器生成的枚举器类是包含4个状态的状态机。
- Before
首次调用MoveNext的初始状态。 - Running
调用MoveNext后进入这个状态。在这个状态中,枚举器检测并设置下一项的位置,遇到yield return、yield break或迭代器体结束进时入下个状态。 - Suspended
状态机等待下次调用MoveNext的状态。 - After
没有更多项可以枚举。