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

人肉反編譯 yield 關鍵字:從狀態機還原到優雅代碼

     編輯:關於.NET

.NET Reflector 雖強,但對 yield 生成的狀態機代碼難以還原為可讀的高級邏輯。本文總結編譯器規律,教你手動「反編譯」 yield 方法。

編譯器轉換規律

  1. 原方法替換為編譯器生成的 IEnumerator<T> 實現類實例。
  2. 含 yield 的邏輯移入該類的 MoveNext() 方法。
  3. 引入狀態機:<>1__state 字段保存當前狀態,MoveNext 用 switch 分發。
  4. 每次 MoveNext 根據狀態跳轉到對應邏輯片段,設置下一狀態並返回 true;結束返回 false。
  5. 參數變為公共字段;局部變量改名為 <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。

實戰:還原 System.Linq.OrderedEnumerable

OrderedEnumerable.GetEnumerator() 使用 yield,Reflector 給出難讀的狀態機。手動還原步驟:

  1. 重命名:<>4__this → this,局部變量按規律還原(buffer、map、i 等)。
  2. 還原 state=0:首個 yield return 前邏輯(構建緩衝區、排序、初始化索引)。
  3. 還原 state=1:後續循環邏輯(索引遞增、邊界檢查、yield return)。
  4. 識別重複模式:發現 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》有更詳盡編譯器規律描述。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved