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

來源:互聯網
上載者:User

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; // Okyield 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 {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();}}

在先前的轉換中,迭代器塊之內的代碼被轉換成state machine,並被放置在列舉程式類的MoveNext方法中。此外局部變數i被轉換成列舉程式對象的一個欄位,因此在MoveNext的調用過程中可以持續存在。
下面的例子列印一個簡單的從整數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();}}}

FromTo方法可被轉換成編譯器產生的可枚舉類的執行個體,該類封裝了迭代器塊中的代碼,如下所示。

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(迭代器)(二)的內容,更多相關內容請關注topic.alibabacloud.com(www.php.cn)!

  • 相關文章

    聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.