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

C# 迭代器模式演進:從 IEnumerable/IEnumerator 到 yield return

     編輯:關於C#

實作 foreach 可迭代物件是 C# 類別庫設計的基本功。C# 1.0 需手動實作 IEnumerable 與 IEnumerator 介面,C# 2.0 引入 yield return 大幅簡化程式碼並提供型別安全。本文以 ListBox 為例,對比兩種時代的寫法。

C# 1.1 手動實作版 (教學用)

using System;
using System.Collections;

public class ListBox : IEnumerable
{
    private string[] _items = new string[8];
    private int _count = 0;

    public ListBox(params string[] initial)
    {
        foreach (var s in initial)
            if (_count < 8) _items[_count++] = s;
    }

    public void Add(string s)
    {
        if (_count < 8) _items[_count++] = s;
    }

    public int Count => _count;

    // IEnumerable.GetEnumerator
    public IEnumerator GetEnumerator()
    {
        return new ListBoxEnumerator(this);
    }

    // 巢狀類別實作 IEnumerator
    private class ListBoxEnumerator : IEnumerator
    {
        private readonly ListBox _parent;
        private int _index = -1;

        public ListBoxEnumerator(ListBox parent) => _parent = parent;

        public bool MoveNext()
        {
            _index++;
            return _index < _parent._count;
        }

        public void Reset() => _index = -1;

        public object Current
        {
            get
            {
                if (_index < 0 || _index >= _parent._count)
                    throw new InvalidOperationException();
                return _parent._items[_index];
            }
        }
    }
}

使用方式:

var lb = new ListBox("A", "B", "C");
lb.Add("D");
foreach (string s in lb) Console.WriteLine(s);

C# 2.0+ yield return 簡化版 (生產環境標準)

using System;
using System.Collections.Generic;

public class ListBox : IEnumerable<string>
{
    private readonly List<string> _items = new List<string>(8);

    public ListBox(params string[] initial)
    {
        _items.AddRange(initial);
    }

    public void Add(string s) => _items.Add(s);
    public int Count => _items.Count;

    // 單一 GetEnumerator 即可,編譯器自動產生狀態機
    public IEnumerator<string> GetEnumerator()
    {
        foreach (var s in _items)
            yield return s;  // 關鍵字:產生迭代器狀態機
    }

    // 非泛型介面顯式實作 (相容舊代碼)
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

yield return 核心特性

  • 狀態機自動生成:編譯器將方法轉換為實作 IEnumerator<T> 的巢狀類別,自動管理 Current、MoveNext、Reset。
  • 延遲執行:呼叫 GetEnumerator() 不會立即執行迴圈,只有在 MoveNext() 時才逐步執行到下一個 yield return。
  • 型別安全:回傳 IEnumerable<T>,foreach 直接取得強型別 T,免拆箱。
  • 可多點 yield:
public IEnumerable<string> GetItems()
{
    yield return "First";
    yield return "Second";
    if (DateTime.Now.Second % 2 == 0)
        yield return "EvenSecond";
    yield return "Last";
}

進階:yield break 提前結束

public IEnumerable<int> GetNumbers(int max)
{
    for (int i = 0; ; i++)
    {
        if (i >= max) yield break; // 結束迭代
        yield return i;
    }
}

效能與記憶體考量

指標手動 IEnumeratoryield return
程式碼行數~50 行~10 行
記憶體配置手控編譯器產生的狀態機類別 (每次迭代一個實例)
延遲執行需自行實作內建
除錯體驗可逐行中斷可逐行中斷 (VS 支援)

最佳實務建議

  • 所有集合類別一律實作 IEnumerable<T> 並使用 yield return。
  • 回傳型別宣告為 IEnumerable<T> 而非具體類別,保持介面導向。
  • 避免在 yield return 方法中混入副作用 (如修改共享狀態),因執行時機不可預期。
  • 需多次枚舉時,考慮 .ToList() 物質化,避免重複計算。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved