IEnumerable + IEnumerator 內部類別,樣板代碼冗長IEnumerator.Current 回傳 object,值型別頻繁裝箱/拆箱castclass 指令驗證型別安全IEnumerable<T>, IEnumerator<T> 強型別,消除裝箱與轉型
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(); // 非泛型介面顯式實作
}
public IEnumerator GetNumbers()
{
for (int i = 1; i < 5; i++)
{
yield return i;
if (i > 2) yield break; // 僅產生 1, 2, 3
}
}
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)。