首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 2.0 規範深度解析:迭代器機制詳解(下)

     編輯:關於C語言

Yield 語句詳細規範

yield-statement:
    yield return expression ;
    yield break ;

yield 非保留字,僅在緊接 return/break 時有特殊意義,其他情況可作識別項。

限制條件

  • 只能出現在方法/運算子/存取子本體(不可在匿名方法內)
  • 不可在 finally 區塊內
  • yield return 不可在包含 catch 子句的 try 區塊內任何位置
  • yield break 允許在 try/catch 區塊內

合法與非法範例

IEnumerator<int> GetEnumerator() {
    try {
        yield return 1; // OK
        yield break;    // OK
    }
    finally {
        yield return 2; // 錯誤:finally 內不可 yield
        yield break;    // 錯誤
    }
    try {
        yield return 3; // 錯誤:try-catch 區塊內不可 yield return
        yield break;    // OK
    }
    catch {
        yield return 4; // 錯誤
        yield break;    // OK
    }
    D d = delegate { yield return 5; }; // 錯誤:匿名方法內不可 yield
}

int MyMethod() { yield return 1; } // 錯誤:回傳型別非列舉器/可列舉介面

執行語意

yield return:評估運算式→隱式轉換至 yield 型別→指派 Current→掛起執行(保存位置/變數,finally 不執行)→狀態→suspended→MoveNext 回傳 true。下次呼叫從掛起處繼續。

yield break:若在 try-finally 內,由內而外執行所有 finally→控制權回呼叫者(MoveNext 或 Dispose)→狀態→after→回傳 false。結束點永不可達。

明確賦值規則

對 yield return expr:

  • expr 開頭的賦值狀態 = 語句開頭狀態
  • 若 expr 結束點明確賦值,則語句結束點也明確賦值;否則不

編譯器實作範例

範例 1:Stack<T> 的 GetEnumerator

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

編譯器轉換為狀態機類別:

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

關鍵轉換:區域變數 i 提升為欄位,迭代器邏輯轉為 switch-goto 狀態機。

範例 2:FromTo 產生可列舉物件(雙重角色)

static IEnumerable<int> FromTo(int from, int to) {
    while (from <= to) yield return from++;
}

編譯器產生同時實作 IEnumerable<int>、IEnumerator<int> 的類別:

class __Enumerable1 : IEnumerable<int>, IEnumerable, IEnumerator<int>, IEnumerator {
    int __state, __current, __from, from, to;
    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;
    }
    // MoveNext/Current/Dispose/Reset 實作略...
}

設計亮點:

  • 首次 GetEnumerator 回傳自身,後續回傳拷貝 → 各列舉器狀態獨立
  • Interlocked.CompareExchange 確保執行緒安全
  • 參數 from 在區塊內修改,引入 __from 保存初始值供拷貝使用
  • __state==0 時呼叫 MoveNext 拋出 InvalidOperationException,防止未先呼叫 GetEnumerator 直接當列舉器用
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved