本文由 AI 分析生成
建立時間: 2026-03-28 來源: https://devblogs.microsoft.com/oldnewthing/20080812-00/?p=21273
Summary
Raymond Chen (The Old New Thing, 2008) explains how the C# compiler transforms yield return iterator methods into state machine classes. Each local variable and parameter becomes a field on a generated class; yield return x becomes state assignment + return true; the MoveNext() method is a switch-based dispatcher over saved states.
Raymond Chen(The Old New Thing,2008)解釋了 C# 編譯器如何將 yield return 迭代器方法轉換為狀態機類。每個局部變量和參數成為生成類的字段;yield return x 變為狀態賦值加 return true;MoveNext() 方法是基於保存狀態的 switch 分發器。
Key Points
- C# iterators are “complex syntactic sugar”: the compiler auto-generates an enumerator class
- Local variables and parameters → member variables of the generated class (including hidden
this) yield return x→current$0 = x; state$0 = n; return true; resume$n:;yield break→state$0 = n2; return false;(terminal state)MoveNext()has a switch dispatcher at the top that jumps to the saved resume point- Efficiency advantage over coroutines/fibers: doesn’t consume a full stack (typically 1MB); instead uses a heap-allocated helper object and borrows the caller’s stack
- The transformation is invisible to most callers but has subtle consequences (discussed in later parts of the series)
Insights
This is a foundational explanation for understanding how C# makes cooperative iteration ergonomic. The key insight: yield return is syntactic sugar for manual state machine construction — the exact pattern a programmer would write before iterators existed. The heap-vs-stack distinction matters for performance-sensitive code: fiber-based coroutines (Python’s earlier model, Windows fibers) pay 1MB stack allocation per coroutine; C#‘s approach pays only the heap allocation for the captured locals. This is the same tradeoff that makes async/await efficient in modern languages.
Connections
Raw Excerpt
The C# method is far more efficient in terms of memory usage since it doesn’t consume an entire stack (typically a megabyte in size) like the fiber approach does. Instead it just borrows the stack of the caller, and anything that it needs to save across calls to MoveNext are stored in a helper object (which goes on the heap rather than the stack).