yield return 是 C# 2.0 引入的迭代器語法,編譯器將其轉換為實作 IEnumerable/IEnumerator 的**狀態機類別**,實現延遲執行 與 流式處理,是 LINQ to Objects 效能基石。
// 立即執行:建立 List 佔用記憶體
IEnumerable Eager()
{
var list = new List();
for (int i = 0; i < 20; i++)
{
Console.WriteLine($"Eager: {i}");
if (i > 2) list.Add(i);
}
return list;
}
// 延遲執行:逐個產生
IEnumerable Lazy()
{
for (int i = 0; i < 20; i++)
{
Console.WriteLine($"Lazy: {i}");
if (i > 2) yield return i;
}
}
// 僅呼叫方法,不迭代
Eager(); // 輸出 0~19
Lazy(); // 無任何輸出!迭代器尚未啟動
// 迭代消費
foreach (var x in Eager()) Console.WriteLine($"Out: {x}");
// 輸出:0~19 (Eager) 然後 3~19 (Out)
foreach (var x in Lazy()) Console.WriteLine($"Out: {x}");
// 輸出:0 (Lazy) 0 (Out) 1 (Lazy) 1 (Out) 2 (Lazy) 2 (Out) 3 (Lazy) 3 (Out) ...
// 交替產生與消費,記憶體常數級
<Lazy>d__0 : IEnumerable, IEnumerator, IDisposable 。<>1__state (狀態:-2=未啟動, -1=結束, 0=啟動中, 1=產生中)、<>2__current (當前值)、<i>5__1 (區域變數 i)。MoveNext() 包含巨大 switch(state),每次呼叫從上一狀態繼續執行至下一 yield return。GetEnumerator() 回傳自身 (或新實例視語境)。Where/Select/Take 串聯形成查詢管線,僅在終端操作 (ToList/foreach) 觸發執行。Take(5) 僅迭代 5 次,後續運算完全跳過。
IEnumerable WithFinally()
{
try
{
for (int i = 0; i < 10; i++)
if (i == 5) yield break; // 提早結束
else yield return i;
}
finally
{
Console.WriteLine("Cleanup"); // 保證執行 (Dispose 時)
}
}
yield break 相當於 state = -1; return false;。finally 區塊在 Dispose() (迭代器結束或 foreach 中斷) 時執行,保證資源釋放。yield return 方法中直接回傳 List/陣列 (失去延遲優勢)。List.Enumerator ) 避免類別分配。