.NET Reflector 雖強,但對 yield 生成的狀態機代碼難以還原為可讀的高級邏輯。本文總結編譯器規律,教你手動「反編譯」 yield 方法。
IEnumerator<T> 實現類實例。yield 的邏輯移入該類的 MoveNext() 方法。<>1__state 字段保存當前狀態,MoveNext 用 switch 分發。MoveNext 根據狀態跳轉到對應邏輯片段,設置下一狀態並返回 true;結束返回 false。<name>X__Y 避免衝突。// 原始
static IEnumerator<int> GetSimple()
{
Console.WriteLine("Creating");
yield return 0;
yield return 1;
yield return 2;
Console.WriteLine("Created");
}
// 反編譯後 MoveNext 核心
switch (state)
{
case 0: state=-1; Console.WriteLine("Creating"); current=0; state=1; return true;
case 1: state=-1; current=1; state=2; return true;
case 2: state=-1; current=2; state=3; return true;
case 3: state=-1; Console.WriteLine("Created"); break;
}
return false;
編譯器為生成緊湊代碼,大量使用 goto 跳轉到對應 Label(即 yield return 處)。結構分為:
switch(state) 跳轉。goto Label 替代 for/if,在 yield return 處插入 Label。OrderedEnumerable.GetEnumerator() 使用 yield,Reflector 給出難讀的狀態機。手動還原步驟:
<>4__this → this,局部變量按規律還原(buffer、map、i 等)。yield return 前邏輯(構建緩衝區、排序、初始化索引)。yield return)。if (i < count) yield return ... 重複,包裝為 for 循環。private IEnumerator<TElement> GetEnumerator()
{
var buffer = new Buffer<TElement>(this.source);
if (buffer.count <= 0) yield break;
var sorter = this.GetEnumerableSorter(null);
var map = sorter.Sort(buffer.items, buffer.count);
for (var i = 0; i < buffer.count; i++)
yield return buffer.items[map[i]];
}
「機械還原」+「模式識別」= 完美反編譯。編譯器追求代碼緊湊導致重複片段,人工合並即可還原優雅邏輯。此技巧源於分析 LINQ 排序實現,《C# in Depth》有更詳盡編譯器規律描述。