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

C# 2.0 Iterator 迭代器改進與編譯器實現原理深度解析

     編輯:關於C語言

背景:C# 1.0 迭代的痛點

  • 需手寫 IEnumerable + IEnumerator 內部類別,樣板代碼冗長
  • IEnumerator.Current 回傳 object,值型別頻繁裝箱/拆箱
  • 參考型別仍需 castclass 指令驗證型別安全

C# 2.0 兩大改進

  1. 泛型介面:IEnumerable<T>, IEnumerator<T> 強型別,消除裝箱與轉型
  2. yield 關鍵字:編譯器自動生成狀態機,極大簡化迭代器實作

泛型迭代器範例


using System.Collections.Generic;

public class Tokens : IEnumerable
{
    private readonly string[] _elements;

    public Tokens(string source, char[] delimiters)
    {
        _elements = source.Split(delimiters);
    }

    public IEnumerator GetEnumerator()
    {
        for (int i = 0; i < _elements.Length; i++)
            yield return _elements[i]; // 編譯器處理狀態
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        => GetEnumerator(); // 非泛型介面顯式實作
}

yield break 提前終止


public IEnumerator GetNumbers()
{
    for (int i = 1; i < 5; i++)
    {
        yield return i;
        if (i > 2) yield break; // 僅產生 1, 2, 3
    }
}

多種迭代方式:屬性回傳 IEnumerable<T>


public class CityCollection
{
    private readonly string[] _cities = { "New York", "Paris", "London" };

    public IEnumerable Reverse
    {
        get
        {
            for (int i = _cities.Length - 1; i >= 0; i--)
                yield return _cities[i];
        }
    }
}

編譯器生成的狀態機 (簡化版)

編譯器將 GetEnumerator 轉換為實作 IEnumerator<T> 的巢狀類別:


// 編譯器生成的偽代碼
private sealed class d__0 : IEnumerator, IEnumerator, IDisposable
{
    private int <>1__state; // 狀態: -1=結束, 0=初始, 1=執行中
    private string <>2__current; // Current 值
    public Tokens <>4__this; // 外部 this 參考
    private int 5__1; // 區域變數 i

    string IEnumerator.Current => <>2__current;
    object IEnumerator.Current => <>2__current;

    bool MoveNext()
    {
        switch (<>1__state)
        {
            case 0:
                <>1__state = -1;
                5__1 = 0;
                goto case 1;
            case 1:
                <>1__state = -1;
                5__1++;
                break;
        }

        if (5__1 < <>4__this._elements.Length)
        {
            <>2__current = <>4__this._elements[5__1];
            <>1__state = 1;
            return true;
        }
        return false;
    }

    void IEnumerator.Reset() => throw new NotSupportedException();
    void IDisposable.Dispose() { }
}

public IEnumerator GetEnumerator()
    => new d__0 { <>4__this = this };

遞迴迭代器:二元樹中序走訪


public class BinaryTree
{
    private Node _root;

    public IEnumerable InOrder => ScanInOrder(_root);

    private IEnumerable ScanInOrder(Node node)
    {
        if (node == null) yield break;

        foreach (var item in ScanInOrder(node.Left))
            yield return item;

        yield return node.Item;

        foreach (var item in ScanInOrder(node.Right))
            yield return item;
    }
}

class Node { public T Item; public Node Left, Right; }

遞迴迭代器的狀態機複雜度

編譯器為每個 yield return 在迴圈中生成狀態,巢狀 foreach 需追蹤子迭代器 (__wrap 欄位) 並在 Dispose 中正確釋放。偽代碼包含多狀態分支 (ScanLeft, GetItem, ScanRight) 與 IDisposable 清理邏輯,展示編譯器以有限狀態機模擬遞迴調用棧。

效能與使用建議

場景建議
單次遍歷、記憶體敏感使用 yield return 延遲執行
需多次遍歷、隨機存取實體化為 List<T> (.ToList())
高效能熱路徑考慮手寫 IEnumerator<T> 避免狀態機開銷
異步資料流.NET Core 3.0+ 使用 IAsyncEnumerable<T> + yield return

總結

yield 是語法糖,編譯器生成實作 IEnumerator<T> 的狀態機類別。理解其原理有助於除錯 (如 MoveNext 中的例外堆疊)、效能分析與正確處理資源釋放 (IDisposable)。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved