繼上一篇介紹匿名方法語法與轉換規則後,本文繼續深入 C# 2.0 規範中關於匿名方法的委托實例相等性、明確賦值規則、方法組隱式轉換以及編譯器實現範例。
規範定義了由匿名方法產生的委托實例在相等運算符(==)與 object.Equals 下的行為:
此規則允許編譯器對語義相同且捕獲相同變量的匿名方法進行優化,返回同一委托實例。
匿名方法對外部變量的明確賦值狀態遵循以下原則:
delegate bool Filter(int i);
void F() {
int max;
// 錯誤:max 在匿名方法聲明處未明確賦值
Filter f = delegate(int n) { return n < max; };
max = 5;
DoWork(f);
}
delegate void D();
void F() {
int n;
D d = delegate { n = 1; };
d();
// 錯誤:n 在此處仍視為未明確賦值
Console.WriteLine(n);
}
類似匿名方法的隱式轉換,方法組(method group)也存在到兼容委托類型的隱式轉換。若 new D(E) 合法,則 E 隱式轉換為 D,等價於 new D(E)。
此特性簡化了事件訂閱語法:
// 原寫法
okButton.Click += new EventHandler(OkClick);
// 簡化後
okButton.Click += OkClick;
方法組與匿名方法均可參與多載決策,但不參與類型推斷。
規範以標準 C# 結構演示編譯器可能的降級實現(非強制性):
class Test {
static void F() {
D d = delegate { Console.WriteLine("test"); };
}
}
// 降級為
class Test {
static void F() { D d = new D(__Method1); }
static void __Method1() { Console.WriteLine("test"); }
}
this 實例成員 → 實例方法class Test {
int x;
void F() { D d = delegate { Console.WriteLine(x); }; }
}
// 降級為
class Test {
int x;
void F() { D d = new D(__Method1); }
void __Method1() { Console.WriteLine(x); }
}
局部變量生命週期必須延長至委托存活期間,編譯器生成封裝類 __Locals1,將變量提升為字段,匿名方法轉為該類的實例方法。
class Test {
void F() {
int y = 123;
D d = delegate { Console.WriteLine(y); };
}
}
// 降級為
class Test {
void F() {
__Locals1 __locals1 = new __Locals1();
__locals1.y = 123;
D d = new D(__locals1.__Method1);
}
class __Locals1 {
public int y;
public void __Method1() { Console.WriteLine(y); }
}
}
this 與多層塊局部變量 → 巢狀封裝類不同作用域的局部變量對應不同封裝類,內層類持有外層類引用,形成鏈式結構。
class Test {
int x;
void F() {
int y = 123;
for (int i = 0; i < 10; i++) {
int z = i * 2;
D d = delegate { Console.WriteLine(x + y + z); };
}
}
}
// 降級為 __Locals1(含 this、y)與 __Locals2(含 z、__Locals1 引用)
理解匿名方法的編譯器降級機制,不僅有助於掌握閉包語義、變量捕獲與生命週期,更能在性能調優與除錯時提供關鍵洞見。下一篇將探討 C# 3.0 引入的 Lambda 表達式如何進一步簡化此模式。