C# 2.0 Specification(迭代器)(一)

22迭代器
22.1迭代器块
迭代器块就是产生值的有序序列的语句块。迭代器块通过一个或多个yield语句区别于常规语句块。

l yield return 语句产生迭代的下一个值。

l yield break 语句指明迭代完成。

迭代器块可以被用作一个方法体(method-body)、运算符体(operator-body)、访问器体(accessor-body),前提是对应函数成员的返回类型是枚举器(enumerator)接口之一或者可枚举(enumerable)接口之一。

迭代器块在C#语法中不什么独特的元素。它们在几个方面受到限制,并且主要的作用在函数成员声明的语义上,但它们在语法上只是语句块而已。

当一个函数成员使用迭代器块实现时,对于正式参数列表指定任何ref或out参数将导致编译时错误。

return语句出现在迭代器块中将导致编译时错误(但yield return语句是允许的)。

在迭代器块中包含不安全上下文(§18.1)将导致编译时错误。即使是当迭代器声明内嵌在不安全上下文中,迭代器块也总是定义为一个安全上下文。

22.1.1枚举器接口
枚举器接口(enumerator interface)是System.Collections.IEnumerator接口以及System.Collections.Generic.IEnumerator<T>的所有实例。在本章,这些接口将相应地作为IEnumerator和IEnumerator<T>而引用。

22.1.2可枚举接口
可枚举接口(enumerable interface)是System.Collections.IEnumerable接口和System.Collections.Generic.IEnumerable<T>的所有实例。在本章,这些接口将相应地作为IEnumerable和IEnumerable<T>而被引用。

22.1.3Yield 类型
迭代器块生成具有相同类型的所有值的序列。给类型被称为迭代器块的yield类型(yield type)。

l 迭代器块的yield类型通常用于实现返回IEnumerator或IEnumerable是对象的函数成员。

l 迭代器块的yield类型通常用于实现返回IEnumerator<T>或IEnumerable<T>是T的函数成员。

22.1.4 this 访问
在类的实例成员的迭代器块内,this表达式被归类为值。该值的类型就是类类型,在这个类型可以采用这种用法,这个值就是成员被调用时的对象的引用。

在结构的实例成员的迭代器块内,this表达时被归类作为一个变量。该变量的类型就是结构类型,在这个结构中它可以采用这种用法。该变量表示一个成员被调用时的对应结构的一个拷贝。在结构实例成员的迭代器块内,this变量的行为就好像是结构类型的一个值参数。

22.2枚举对象
当返回枚举器接口类型的函数成员使用迭代器块实现时,调用函数成员并不会立即执行迭代器块中的代码。相反,枚举器对象(enumerator object)将被创建和返回。该对象封装了在迭代器块中指定的代码,当枚举器对象的MoveNext方法被调用时,迭代器块中的代码就会执行。枚举器对象有如下的特征。

l 它实现了IEnumerator和IEnumerator<T>,T是迭代器块的yield类型(产生类型)。

l 它实现了System.IDisposable。

l 它被使用实参值(如果有的话)的拷贝而初始化,而实例值将被传递给函数成员。

l 它有四个潜在的状态before、running、suspended和after,并且它在before状态之前被初始化。

枚举器对象通常是一个编译器生成的枚举器类实例,它封装了迭代器语句块中的代码,并且实现了枚举器接口,但其它实现方法也是可以的。如果一个枚举器类是由编译器生成的,这个类将会是内嵌的,在包含函数成员的类中,类将具有私有可访问性,并且该类具有一个保留为编译器所用的名字(§2.4.2)。

枚举器对象可以实现比在此指定的更多接口。

随后几节描述了由IEnumerable和IEnumerable<T>接口实现的MoveNext、Current、和Dispose成员的确切行为,这两个接口由枚举对象提供。

请注意,枚举器对象并不支持IEnumerator.Reset方法。调用该方法将会抛出System.NotSupportedException异常。

22.2.1MoveNext方法
枚举器对象的MoveNext方法封装了迭代器块的代码。调用MoveNext方法将执行迭代器内的代码,并将枚举对象的Current属性设置为合适的值。由MoveNext方法执行的精确动作,取决于当MoveNext方法被调用时枚举器对象的状态。

l 如果枚举器对象状态是before,调用MoveNext

n 将把状态改为running。

n 将把迭代器块的参数(包括this)初始化为,当枚举器对象被初始化而保存的实参值和实例值。

n 从开始执行迭代器块直到执行被中断(如下面所描述的)。

l 如果枚举器对象的状态是running,调用MoveNext的结果是未指定的。

l 如果枚举器对象的状态是suspended,调用MoveNext

n 将把状态改为running。

l 恢复所有局部变量和参数(包括this)的值为迭代器最后一次挂起(suspended)时执行状态的值。请注意,由这些变量所引用的任何对象的内容,都可能因为前一次对MoveNext的调用而改变。

n 在引发执行挂起的yield return 语句之后重新开始执行迭代器块,并且这个状态会继续直到执行被中断(如下所描述)。

l 如果枚举器对象的状态是after,那么调用MoveNext将返回false。

当MoveNext执行迭代器块时,有四种方法可以中断执行:通过一个yield return 语句,通过一个yield break语句,到达迭代器块的结束点,以及一个异常被抛出,并被传播到迭代器块之外。

l 当遇到一个yield return 语句时(§22.4),将会发生如下情况

n 在该语句中被给定的表达式将被计算,隐式地转换到产生类型(yield type),并被赋值给枚举对象的Current属性。

n 迭代器体的执行将被挂起。所有局部变量的值和参数(包括this)被保存,该yield return 语句的位置也被保存。如果yield return 语句在一个或多个try块之内,与之关联的finally块在此时将不会执行。

n 枚举器对象的状态被改为suspended。

n MoveNext方法对调用方返回true,表明迭代器成功前进到下一个值。

l 当遇到yield break 语句时,将会发生如下情况

n 如果yield break 语句在一个或多个try块之内,与之关联的finally语句将被执行。

n 枚举器对象的状态被改为after。

n MoveNext方法对调用方返回false,表明迭代已经完成。

l 当遇到迭代器块的结束点时,将会发生如下情况。

n 枚举器对象的状态被改为after。

n MoveNext方法对调用方返回false,表明迭代已经完成。

l 当一个异常被抛出并被传播到迭代器块之外时,将会发生如下情况。

n 在迭代器块之内将会由于异常传播(exception propagation)而执行合适的finally块。

n 枚举器对象的状态被改为after。

n 对于MoveNext方法的调用方来说,异常传播将会继续。

22.2.2 Current属性
枚举器对象的Current属性受到迭代器块的yield return 语句的影响。

当枚举器对象处于suspended状态时,Current的值就是最后一次调用MoveNext时被设置的值。当枚举器对象处于before、running或after状态时,访问Current的所得结果是未指定的。

对于一个具有非object类型的yield 类型迭代器块,通过枚举器对象的IEnumerable实现访问Current所得实现,对应于通过枚举器对象的IEnumerator<T>访问Current所得实现,并将结果转换到object类型。

22.2.3 Dispose方法
Dispose方法通过将枚举器对象的状态置为after,以清理迭代结果。

l 如果枚举器对象的状态是before,调用Dispose将改变其状态为after。

l 如果枚举器对象的状态是running,调用Dispose的结果是为指定的。

l 如果枚举器对象的状态是suspended,调用Dispose将

n 改变其状态为running。

n 执行finally块,就好像最后执行的yield return语句是一个yield break语句。如果这里引发一个异常被抛出并传播到迭代器体之外,枚举器对象的状态将被置为after,并且该异常将被传播给Dispose方法的调用方。

n 改变其状态为after。

l 如果枚举器对象的状态为after,调用Dispose没有效果。

22.3可枚举对象
当返回一个可枚举接口类型的函数成员使用迭代器块实现时,调用函数成员不会立即执行迭代器块代码。相反,一个可枚举对象(enumerable object)将被创建并返回。可枚举对象的GetEnumerator方法返回一个枚举器对象,它封装了在迭代器块中指定的代码,当枚举器对象的MoveNext方法被调用时,将触发迭代器块代码的执行。可枚举对象具有如下特征。

l 它实现了IEnumerable和IEnumerable<T>接口,这里T是迭代器块的产生类型(yield type)。

l 它使用实参值的拷贝进行初始化(如果有的话),并将实例值传递给函数成员。

可枚对象通常是一个由编译器生成的可枚举类的实例,该类封装了迭代器块的代码,并实现了可枚举接口,但其他实现方法也是可以的。如果可枚举类由编译器生成,该类将内嵌在包含函数成员的类中,并具有私有可访问性,以及一个为编译器所保留使用的名字(§2.4.2)。

可枚对象可以实现比在此说明的更多接口。特别的是,可枚举对象也可以实现IEnumerator和IEnumerator<T>接口,这使得它既可以作为一个可枚举对象又可作为枚举器对象。在那个实现类型中,对可枚举对象的GetEnumerator方法的首次调用,将返回可枚举对象自身。对于该可枚举对象的GetEnumerator方法的后续调用,如果有的话,将会返回可枚举对象的一个拷贝。因此,每次返回的枚举器将有它自己的状态,并且在一个枚举器中所作的改变不会影响另一个枚举器。

22.3.1 GetEnumerator方法
可枚举对象提供了IEnumerable和IEnumerable<T>接口的GetEnumerator方法的一个实现。这两个GetEnumerator方法共享一个公共实现,它是用来得到并返回一个有效的枚举器对象。

枚举器对象使用实参值进行初始化,当可枚举对象被初始化时其实例值将被保存,另一方面,枚举器对象函数将如§22.2所描述。

时间: 2024-10-01 13:15:02

C# 2.0 Specification(迭代器)(一)的相关文章

C# 2.0 Specification(迭代器)(二)

22.4 yield 语句yield语句用于迭代器块以产生一个枚举器对象值,或表明迭代的结束. embedded-statement:(嵌入语句)...yield-statement(yield语句) yield-statement:(yield 语句)yield return expression ;yield break ; 为了确保和现存程序的兼容性,yield并不是一个保留字,并且 yield只有在紧邻return或break关键词之前才具有特别的意义.而在其他上下文中,它可以被用作标识

C# 2.0 Specification(一)简介

19.C#2.0介绍C#2.0引入了几项语言扩展,其中最重要的是泛型.匿名方法.迭代器和不完整类型(partial type). 泛型可以让类.结构.接口.委托和方法,通过他们所存储和操纵的数据的类型被参数化.泛型是很有用的,因为他们提供了更强的编译时类型检查,减少了数据类型之间的显式转换,以及装箱操作和运行时类型检查. 匿名方法可以让代码块以内联的方式潜入到期望委托值的地方.匿名方法与Lisp 编程语言中的λ函数(lambda function)相似.C#2.0支持"closures"

C# 2.0 Specification(二)

19.1.5泛型方法在某些情形下,类型参数对于整个类不是必需的,而只对特定方法内是必需的.经常,当创建一个接受泛型类型作为参数的方法时就是这样.例如,当使用早先描述的Stack<T>类时,一个通用的模式可能是在一行中压入多个值,在一个单一的调用中写一个方法这么做也是很方便的.对于特定的构造类型,例如Stack<int>,这个方法看起来像这样. void PushMultiple(Stack<int> stack ,params int[] values) { forea

C# 2.0 Specification(匿名方法)(一)

21匿名方法21.1.匿名方法表达式匿名方法表达式(anonymous-method-expression)定义了匿名方法(anonymous method),它将计算为引用该方法的一个具体值. l primary-no-array-creation-expression(基本非数组创建表达式:)-anonymous-method-expression(匿名方法表达式) l anonymous-method-expression:delegate anonymous-method-signatu

C# 2.0 Specification(匿名方法)(二)

21.7委托实例相等性如下规则适用由匿名方法委托实例的相等运算符(§7.9.8)和object.Equals方法产生的结果. l 当委托实例是由具有相同被捕获外部变量集合的语义相同的匿名方法表达式计算而产生时,可以说(但不是必须)它们相等. l 当委托实例由具有语义不同的匿名方法表达式,或具有不同的被捕获外部变量集合时,它们决不相等. 21.8明确赋值匿名方法参数的明确赋值状态与命名方法是相同的.也就是,引用参数和值参数被明确的赋初值,而输出参数不用赋初值.并且,输出参数在匿名方法正常返回之前必

C#2.0 Specification(泛型一)

这篇文章是翻译的微软的技术文章.供学习c#的朋友参考,请勿用于商业目的.http://msdn.microsoft.com/vcsharp/team/language/default.aspx由于这一章非常长可能需要分几篇:)20.泛型20.1泛型类声明泛型类声明是一个需要提供类型参数以形成实际类型的类的声明. 类声明可以有选择地定义类型参数. class-declaration: (类声明) attributesopt class-modifiersopt class identifierop

C# 2.0 Specification (泛型四)

接泛型三这篇文章是翻译的微软的技术文章.供学习c#的朋友参考,请勿用于商业目的.http://msdn.microsoft.com/vcsharp/team/language/default.aspx20.6泛型方法泛型方法是与特定类型相关的方法.除了常规参数,泛型方法还命名了在使用方法时需要提供的一组类型参数.泛型方法可以在类.结构或接口声明中声明,而它们本身可以是泛型或者非泛型的.如果一个泛型方法在一个泛型类型声明中被声明,那么方法体可以引用方法的类型参数和包含声明的类型参数. class-

C# 2.0 Specification (四)

这段比较短就先干掉了:)23不完整类型23.1不完整类型声明新类型修饰符partial 用于在多个部分中定义一个类型.为了确保和现存程序的兼容性,这个修饰符和其他修饰符(比如get和set)是不同的,它不是一个关键字,并且它必须紧邻出现在关键字class ,struct或者interface之前. l class-declaration(类声明)attributes opt class-modifiers opt partialopt class identifier type-paramete

C#2.0 Specification(泛型二)

接(泛型一)这篇文章是翻译的微软的技术文章.供学习c#的朋友参考,请勿用于商业目的.http://msdn.microsoft.com/vcsharp/team/language/default.aspx20.1.6泛型类中的静态构造函数在泛型类中的静态构造函数被用于初始化静态字段,为每个从特定泛型类声明中创建的不同的封闭构造类型,执行其他初始化.泛型类型声明的类型参数在作用域之内,可以在静态构造函数体内被使用. 如果下列情形之一发生,一个新的封闭构造类类型将被首次初始化. 一个封闭构造类型的实