1.枚举类型和枚举器
对于有枚举器的类型而言,必须有一个方法来获取它,获取一个对象枚举器的方法是调用对象的GetEnumerator方法,实现GetEnumerator方法的类型叫做可枚举类型
foreach结构设计用来和可枚举类型一起使用,只要给它的遍历对象是可枚举类型,比如数组,它就会执行如下行为:
- 通过调用GetEnumerator方法获取对象的枚举器
- 从枚举器中请求每一项并且把它作为迭代变量,代码可以读取该变量但不可以改变
2.IEnumerator接口
实现了IEnumerator接口的枚举器包含3个函数成员:Current,MoveNext以及Reset
- Current是返回序列中当前位置项的属性。
- - 它是只读属性
- - 它返回object类型的引用,所以可以返回任何类型
- MoveNext是把枚举类位置前进到集合中下一项的方法,它也返回布尔值,指示新的位置是有效位置还是已经超过了序列的尾部
- - 如果新的位置是有效的,方法返回true
- - 如果新的位置是无效的(比如当前位置到达了尾部),方法返回false
- - 枚举器的原始位置在序列中的第一项之前,因此MoveNext必须在第一次使用Current之前调用
- Reset是把位置重置为原始状态的方法
3.IEnumerable接口
可枚举类是指实现了IEnumerable接口的类,IEnumerable接口只有一个成员——GetEnumerator方法,它返回对象的枚举器
可枚举类的声明形式:
using System.Collection;
class MyClass:IEnumerable//实现IEnumerable接口
{
public IEnumerator GetEnumerator{...}//返回IEnumerator类型的对象
...
}
4.泛型枚举接口
- 对于非泛型接口形式
-
- IEnumerable接口的GetEnumerator方法返回实现IEnumerator枚举器类的实例
-
- 实现IEnumerator的类实现了Current属性,它返回object的引用,然后我们必须把它转化为实际类型的对象
- 对于泛型接口形式
-
- IEnumerable接口的GetEnumerator方法返回实现IEnumator的枚举器类的实例
-
- 实现IEnumerator的类实现了Current属性,它返回实际类型的对象,而不是object基类的引用
5.迭代器
我们可以把手动编码的可枚举类型和枚举器替换为由迭代器生成的可枚举类型和枚举器。
1. 迭代器块
迭代器块是由一个或多个yield语句的代码块,下面3种类型的代码块种的任意一种都可以是迭代器块:
- 方法主体
- 访问器主体
- 运算符主体
迭代器块与其他代码不同,其他块包含的语句被当作是命令式的,也就是说,先执行代码块的第一个语句,然后执行后面的语句,最后控制离开块,另一方面,迭代器块不是需要在同一时间执行的一串命令式命令,而是描述了希望编译器为我们创建的枚举器类的行为,迭代器块中的代码描述了如何枚举元素。
迭代器块由两个特殊语句
- yield return语句指定了序列中返回的下一项
- yield break语句指定在序列中没有其他项
public IEnumerator<string> IteratorMethod()//产生枚举器的迭代器
{
...
yield return...;
}
public IEnumerable<string> IteratorMethod()//产生可枚举类类型的迭代器
{
...
yield return...;
}
- 使用迭代器来创建枚举器
class MyClass
{
public IEnumerator<string> GetEnumerator()//实现了这个方法,所以类是可枚举类型,在foreach语句中就可以直接使用它的实例
{
return BlackAndWhite();//返回枚举器
}
public IEnumerator<string> BlackAndWhite()//迭代器,为MyClass类产生返回枚举器的方法
{
yield return "black";
yield return "gray";
yield return "white"
}
}
class Program
{
static void Main()
{
MyClass mc=new MyClass();
foreach(string shade in mc)
Console.WriteLine(shade);
}
}
- 使用迭代器来创建可枚举类型
class MyClass
{
public IEnumerator<string> GetEnumerator()//实现了这个方法,所以类是可枚举类型,在foreach语句中就可以直接使用它的实例
{
IEnumerable<string> myEnumerable=BlackAndWhite();//获取可枚举类型
return myEnumerable.GetEnumerator();//获取枚举器
}
public IEnumerable<string> BlackAndWhite()//迭代器
{
yield return "black";
yield return "gray";
yield return "white"
}
}
class Program
{
static void Main()
{
MyClass mc=new MyClass();
foreach(string shade in mc)//使用类对象
Console.WriteLine("{0}",shade);
foreach(string shade in mc.BlackAndWhtie())
Console.WriteLine("{0}",shade);//使用类枚举器方法
}
}
6.常见迭代器模式
- 当我们实现返回枚举器的迭代器时,必须通过实现GetEnumerator来让类可枚举,它返回由迭代器返回的枚举器
- 如果我们在类中实现迭代器返回可枚举类型,我们可以让类实现GetEnumerator来让类本身可被枚举,或不实现GetEnumerator,让类不可枚举
-
- 如果实现,让它调用迭代器方法以获取自动生成的实现IEnumerable的类实例,然后,从IEnumerable对象返回由GetEnumerator创建的枚举器
-
- 如果通过不实现使类本身不可枚举,仍然可以使用由迭代器返回的可枚举类,只需要直接调用迭代器方法
7.将迭代器作为属性
public IEnumerator<string> GetEnumerator()
{
return _listFromUVtoIR
?UVtoIR
:IRtoUV;
}
public IEnumerator<string> UVtoIR
{
get
{
for(int i=0;i<colors.length;i++)
yield return colors[i];
}
}
public IEnumerator<string> IRtoUV
{
get
{
for(int i=colors.length-1;i>=0;i--)
yield return colors[i];
}
}
8.其他
- 迭代器需要System.Collections.Generic命名空间,因此需要using指令引入它
- 在编译器生成的枚举器中,Reset方法没有实现,而它是接口需要的方法,因此调用时总是抛出System.NotSupportedException
- Before 首次调用MoveNext的初始状态
- Running 调用MoveNext后进入这个状态,在这个状态中,枚举器检测并设置下一项的位置,在遇到yield return,yield break或在迭代器体结束时,退出状态
- Suspended 状态机等待下次调用MoveNext的状态
- After 没有更多项可以枚举
如果状态机在Before或Suspended状态时调用了MoveNext方法,就转到了Running状态,在Running状态中,它检测集合的下一项并设置位置
如果有更多项,状态机会转入Suspended状态,如果没有更多项,它转入并保持在After状态.