C# 匿名方法/ Lambda 底層由編譯器降級為「顯示類別」機制實現閉包語義。理解其 IL 層面運作,能精準預測捕獲語義、記憶體行為與效能特徵。
__DisplayClass<ID>)this,生成方法為 static
// 來源
void M() {
int i = 0;
Action a = delegate { i++; };
a();
}
// 降級 (簡化 IL)
sealed class __DisplayClass0 {
public int i;
public void __Anonymous0() => this.i++;
}
void M() {
var d = new __DisplayClass0 { i = 0 };
Action a = new Action(d.__Anonymous0);
a();
}
// 來源
void M() {
int outer = 0;
for (int i=0; i<10; i++) {
int inner = i;
Action a = delegate {
Console.WriteLine($"outer={outer++}, i={i}, inner={++inner}");
};
actions[i] = a;
}
}
// 降級結構 (三層)
class __DisplayClassOuter { public int outer; }
class __DisplayClassFor { public int i; }
class __DisplayClassInner {
public int inner;
public __DisplayClassOuter outerRef;
public __DisplayClassFor forRef;
public void __Anonymous() {
Console.WriteLine($"outer={outerRef.outer++}, i={forRef.i}, inner={++inner}");
}
}
outer:方法作用域 → __DisplayClassOuteri:for 作用域 → __DisplayClassForinner + 匿名方法:迴圈體作用域 → __DisplayClassInner,持有外層兩類別引用__DisplayClassInner,共享 Outer/For 實例for(int i=0;...) 中 i 單一實例共享,所有委派見到最終值。C# 5+ foreach 修正為每次迭代新變數編譯器根據委派目標型別推斷匿名方法參數:
button.Click += delegate { ... }; // 推斷為 EventHandler(object, EventArgs)
button.Click += (s,e) => { ... }; // 同等
// Delphi 巢狀函數
procedure SayHello;
var Name: string;
procedure Say; begin WriteLn(Name); end;
begin Name := 'Flier'; Say; end;
// Delphi 降級:自動記錄型別 + 參數傳遞
type $Unnamed1 = record Name: string; end;
procedure @1$SayHello$Say(var P: $Unnamed1); begin WriteLn(P.Name); end;
procedure SayHello;
var Name: string; U: $Unnamed1;
begin Name:='Flier'; U.Name:=Name; @1$SayHello$Say(U); end;
WeakReference 或手動清理struct 顯示類別(C# 11+ ref struct)或手寫具名方法__DisplayClass 實例,助理理解捕獲狀態掌握 CLR 閉包實現本質,是寫出高效、正確、可診斷非同步/事件驅動代碼的內功心法。