日期:2013-01-23  浏览次数:20432 次

22.4 yield 语句
yield语句用于迭代器块以产生一个枚举器对象值,或表明迭代的结束。

embedded-statement:(嵌入语句)
...
yield-statement(yield语句)

yield-statement:(yield 语句)
yield return expression ;
yield break ;

为了确保和现存程序的兼容性,yield并不是一个保留字,并且 yield只有在紧邻return或break关键词之前才具有特别的意义。而在其他上下文中,它可以被用作标识符。

yield语句所能出现的地方有几个限制,如下所述。

l yield语句出现在方法体、运算符体和访问器体之外时,将导致编译时错误。

l yield语句出现在匿名方法之内时,将导致编译时错误。

l yield语句出现在try语句的finally语句中时,将导致编译时错误。

l yield return 语句出现在包含catch子语句的任何try语句中任何位置时,将导致编译时错误。

如下示例展示了yield语句的一些有效和无效用法。

delegate IEnumerable<int> D();

IEnumerator<int> GetEnumerator() {
try {
yield return 1; // Ok
yield break; // Ok
}
finally {
yield return 2; // 错误, yield 在finally中
yield break; // 错误, yield 在 finally中
}





try {
yield return 3; // 错误, yield return 在try...catch中
yield break; // Ok
}
catch {
yield return 4; // 错误, yield return 在 try...catch中
yield break; // Ok
}

D d = delegate {
yield return 5; // 错误, yield 在匿名方法中
};
}

int MyMethod() {
yield return 1; // 错误, 迭代器块的错误返回类型
}



从yield return 语句中表达式类型到迭代器的产生类型(§22.1.3),必须存在隐式转换(§6.1)。

yield return 语句按如下方式执行。

l 在语句中给出的表达式将被计算(evaluate),隐式地转换到产生类型,并被赋给枚举器对象的Current属性。

l 迭代器块的执行将被挂起。如果yield return 语句在一个或多个try块中,与之关联的finally块此时将不会执行。

l 枚举器对象的MoveNext方法对调用方返回true,表明枚举器对象成功前进到下一个项。



对枚举器对象的MoveNext方法的下一次调用,重新从迭代器块挂起的地方开始执行。

yeld break 语句按如下方式执行。

l 如果yield break 语句被包含在一个或多个带有finally块的try块内,初始控制权将转移到最里面的try语句的finally块。当控制到达finally块的结束点后,控制将会转移到下一个最近的try语句的finally块。这个过程将会一直重复直到所有内部的try语句的finally块都被执行。

l 控制返回到迭代器块的调用方。这可能是由于枚举器对象的MoveNext方法或Dispose方法。



由于yield break语句无条件的转移控制到别处,所以yield break语句的结束点将永远不能到达。



22.4.1明确赋值
对于以yield return expr 形式的yield return 语句stmt



l 像stmt开始一样,在expr的开头变量v具有明确的赋值状态。

l 如果在expr的结束点v被明确赋值,那它在stmt的结束点也将被明确赋值;否则,在stmt结束点将不会被明确赋值。



22.5实现例子
本节以标准C#构件的形式描述了迭代器的可能实现。此处描述的实现基于与Microsoft C#编译器相同的原则,但这绝不是强制或唯一可能的实现。

如下Stack<T>类使用迭代器实现了GetEnumerator方法。该迭代器依序枚举了堆栈中从顶到底的元素。

using System;
using System.Collections;
using System.Collections.Generic;

class Stack<T>: IEnumerable<T>
{
T[] items;
int count;

public void Push(T item) {
if (items == null) {
items = new T[4];
}
else if (items.Length == count) {
T[] newItems = new T[count * 2];
Array.Copy(items, 0, newItems, 0, count);
items = newItems;
}
items[count++] = item;
}

public T Pop() {
T result = items[--count];
items[count] = T.default;
return result;
}

public IEnumerator<T> GetEnumerator() {
for (int i = count - 1; i >= 0; --i) yield items[i];
}
}





GetEnumerator方法可以被转换到编译器生成的枚举器类的实例,该类封装了迭代器块中的代码,如下所示。

class Stack<T>: IEnumerable<T>
{
...

public IEnumerator<T> GetEnumerator() {
return new __Enumerator1(this);
}

class __Enumerator1: IEnumerator<T>, IEnumerator
{
int __state;
T __current;
Stack<T> __this;
int i;

public __Enumerator1(Stack<T> __this) {
this.__this = __this;
}

public T Current {