22.4 yield 語句
yield語句用於迭代器區塊以產生一個枚舉器物件值,或表示迭代的結束。
embedded-statement:(嵌入語句)
...
yield-statement(yield語句)
yield-statement:(yield 語句)
yield return expression ;
yield breakbreak並不是一個保留字,而且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 語句如下執行。
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語句的結束點將永遠無法到達。
l 像stmt開始一樣,在expr的開頭變數v具有明確的賦值狀態。
l 如果在expr的結束點v被明確賦值,那麼它在stmt的結束點也將被明確賦值;否則,在stmt結束點將不會被明確賦值
如下Stackusing 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];
}
}
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 { get { return __current; } } object IEnumerator.Current { get { return __current; } } public bool MoveNext() { switch (__state) { case 1: goto __state1; case 2: goto __state2; } i = __this.count - 1; __loop: if (i < 0) goto __state2; __current = __this.items[i]; __state = 1; return true; __state1: --i; goto __loop; __state2: __state = 2; return false; } public void Dispose() { __state = 2; } void IEnumerator.Reset() { throw new NotSupportedException(); } }
下面的範例列印一個簡單的從整數1到10的乘法表。此範例中FromTo方法傳回一個可枚舉對象,並且使用迭代器實作。
using System; using System.Collections.Generic; class Test { static IEnumerable<int> FromTo(int from, int to) { while (from <= to) yield return from++; } static void Main() { IEnumerable<int> e = FromTo(1, 10); foreach (int x in e) { foreach (int y in e) { Console.Write("{0,3} ", x * y); } Console.WriteLine(); } } }
using System; using System.Threading; using System.Collections; using System.Collections.Generic; class Test { ... static IEnumerable<int> FromTo(int from, int to) { return new __Enumerable1(from, to); } class __Enumerable1: IEnumerable<int>, IEnumerable, IEnumerator<int>, IEnumerator { int __state; int __current; int __from; int from; int to; int i; public __Enumerable1(int __from, int to) { this.__from = __from; this.to = to; } public IEnumerator<int> GetEnumerator() { __Enumerable1 result = this; if (Interlocked.CompareExchange(ref __state, 1, 0) != 0) { result = new __Enumerable1(__from, to); result.__state = 1; } result.from = result.__from; return result; } IEnumerator IEnumerable.GetEnumerator() { return (IEnumerator)GetEnumerator(); } public int Current { get { return __current; } } object IEnumerator.Current { get { return __current; } } public bool MoveNext() { switch (__state) { case 1: if (from > to) goto case 2; __current = from++; __state = 1; return true; case 2: __state = 2; return false; default: throw new InvalidOperationException(); } } public void Dispose() { __state = 2; } void IEnumerator.Reset() { throw new NotSupportedException(); } } }
这个可枚举类实现了可枚举接口和枚举器接口,这使得它成为可枚举的或枚举器。当GetEnumerator方法被首次调用时,将返回可枚举对象自身。后续可枚举对象的GetEnumerator调用,如果有的话,都返回可枚举对象的拷贝。因此,每次返回的枚举器都有其自身的状态,改变一个枚举器将不会影响另一个。Interlocked.CompareExchange方法用于确保线程安全操作。
from和to参数被转换为可枚举类的字段。由于from在迭代器块内被修改,所以引入另一个__from字段来保存在每个枚举其中from的初始值。
如果当__state是0时MoveNext被调用,该方法将抛出InvalidOperationException异常。这将防止没有首次调用GetEnumerator,而将可枚举对象作为枚举器而使用的现象发生。
(C# 2.0 Specification 全文完)
以上就是C# 2.0 Specification(迭代器)(二)的内容,更多相关内容请关注PHP中文网(www.php.cn)!