日期:2013-02-14  浏览次数:20445 次

4.1 迭代器块
一个迭代器块(iterator block)是一个能够产生有序的值序列的块。迭代器块和普通语句块的区别就是其中出现的一个或多个yield语句。

yield return语句产生迭代的下一个值。
yield break语句表示迭代完成。
只要相应的函数成员的返回值类型是一个枚举器接口(见4.1.1)或是一个可枚举接口(见4.1.2),一个迭代器块就可以用作方法体、运算符体或访问器体。

迭代器块并不是C#语法中的独立元素。它们受多种因素的制约,并且对函数成员声明的语义有很大影响,但在语法上它们只是块(block)。

当一个函数成员用一个迭代器块来实现时,如果函数成员的形式参数列表指定了ref或out参数,则会引起编译错误。

如果在迭代器块中出现了return语句,则会因此编译错误(但yield return语句是允许的)。

如果迭代器块包含不安全上下文,则会引起编译错误。一个迭代器块必须定义在一个安全的上下文中,即使它的声明嵌套在一个不安全上下文中。

4.1.1 枚举器接口
枚举器(enumerator)接口包括非泛型接口System.Collections.IEnumerator和泛型接口System.Collections.Generic.IEnumerator<T>的所有实例化。在这一章中,这些接口将分别称作IEnumerator和IEnumerator<T>。

4.1.2 可枚举接口
可枚举(enumerable)接口包括非泛型接口System.Collections.IEnumerable和泛型接口System.Collections.Generic.IEnumerable<T>。在这一章中,这些接口分别称作IEnumerable和IEnumerable<T>。

4.1.3 生成类型
一个迭代器块能够产生一个有序的值序列,其中所有的制具有相同的类型。这个类型称为迭代器块的生成类型(yield type)。

用于实现一个返回IEnumerator或IEnumerable的函数成员的迭代器块的生成类型为object。
用于是下一个返回IEnumerator<T>或IEnumerable<T>的函数成员的迭代器块的生成类型为T。
4.1.4 this访问
在一个类的一个实例成员中的迭代器块里,表达式this是一个值。这个值的类型就是出现这种用法的类,并且它是对被调用的方法所在的对象的一个引用。

在一个结构的一个实例成员中的迭代器块里,表达式this是一个变量。这个变量的类型就是出现这种用法的结构。这个变量存贮了对被调用成员所在结构的一个拷贝。结构的实例成员中的迭代器块里的this变量和以该结构为类型的值变量完全一样。

4.2 Enumerator对象
如果一个函数成员使用了迭代器块来返回一个枚举器接口类型,对该函数成员的调用不会立即执行迭代器块中的代码,而是建立并返回一个枚举器对象。这个对象封装了迭代器块中指定的代码,而对迭代器中指定的代码的执行发生在调用该枚举器对象的MoveNext()方法时。一个枚举器对象具有如下特征:

它实现了IEnumerator和IEnumerator<T>,这里T是迭代器块的生成类型。
它实现了System.IDisposable。
它用传递给函数成员的参数值(如果有的话)和实例值进行初始化。
它有四个可能的状态:before、running、suspended和after,其初始状态为before。
典型的枚举器对象是由编译器自动生成的封装了迭代器块中的代码并实现了枚举器接口的枚举器类的实例,但其他的实现也是允许的。如果一个枚举器类是由编译器自动生成的,则该类是直接或间接地嵌套在函数成员中的,具有私有的可访问性,并且具有一个由编译器保留使用的名字。

一个枚举器对象可以实现上面所述之外的其它接口。

后面的几节详细地描述了由一个枚举器对象所实现的IEnumerable和IEnumerable<T>接口中的MoveNext()、Current和Dispose()成员的确切行为。

注意,枚举器对象不支持IEnumerator.Reset()方法。调用该方法会抛出System.NotSupporteException异常。

4.2.1 MoveNext()方法
枚举器对象的MoveNext()方法封装了迭代器块的代码。对MoveNext()方法的调用执行了迭代器块中的代码,并为枚举器对象的Current属性设置一个适当的值。MoveNext()方法完成得确切动作取决于调用MoveNext()方法是枚举器对象的状态:

如果枚举器对象的状态为before,调用MoveNext()方法:
将状态设置为running。
将迭代器块对象的参数(包括this)初始化为枚举器对象初始化时所保存的变量值和实例值。
从执行迭代器块的开始执行,直到被中断(将在下面讨论)。
如果枚举器对象的状态为running,则调用MoveNext()方法的结果未指定。
如果枚举器对象的状态为suspended,调用MoveNext()方法:
将状态设置为running。
将所有局部变量和参数(包括this)恢复为迭代器块执行过程中最后一次挂起时所保存的值。注意这些变量所引用的对象的内容在上一次MoveNext()后可能会发生改变。
从上一次执行中断所在的yield return语句继续执行迭代器块,直到执行再次被中断(将在下面讨论)。
如果枚举器对象的状态为after,调用MoveNext()方法将返回false。
当MoveNext()方法执行迭代器块时,执行过程会通过四种途径中断:yield return语句、yield break语句、遇到迭代器块的结尾以及迭代器块中抛出了异常并被传播到块外。

当遇到yield return语句(见4.4)时:
对语句中给定的表达式进行求值,隐式转换为生成类型,并赋给枚举器对象的Current属性。
挂起迭代器体的执行过程。保存所有局部变量和参数(包括this)的值,以及这个yield return语句的位置。如果该yield return语句位于一个或多个try块中,则与之相关联的finally块在此时还不会被执行。
将枚举器对象的状态设置为suspended。
向MoveNext()方法的调用者返回true,表示迭代已经成功地转移到下一个值上。
当遇到yield break语句(见4.4)时:
如果该yield break语句位于一个或多个try块中,则执行与之相关联的finally块。
将枚举器对象的状态设置为after。
向MoveNext()方法的调用者返回false,表示迭代完成。
当遇到迭代器快的结尾时:
将枚举器对象的状态设置为after。
向MoveNext()方法的调用者返回false,表示迭代完成。
当迭代器快报出了一个异常,并传播到块外时:
迭代器块中适当的finally块将被执行。
将枚举器对象的状态设置为after。
将异常传播给MoveNext()方法的调用者。
4.2.2 Current属性
一个枚举器对象的Current属性受迭代器块中的yield return语句的影响。

当一个枚举器对象处于suspended状态时,Current属性的值由最后一次对MoveNext()方法的调用设置。当一个枚举器对象处于before、running或after状态时,访问Current属性的结果是未定义的。

如果一个迭代器块的生成类型不是object,通过枚举器对象实现的IEnumerable以及相应的IEnumerator<T>对Current的访问会将结果转换为object。

4.2.3 Dispose()方法
Dispose()方法通过将枚举器对象的状态设置为after来清除迭代器。

如果枚举器对象的状态为before,调用Dispose()方法将其状态设置为after。
如果枚举器对象的状态为running,调用Dispose()方法的结果是未定义的。
如果枚举器对象的状态为suspended,调用Dispose()方法:
将状态设置为running。
执行所有的finally块,好像yield return语句是yield break语句一样。如果这