在 C# 2.0 中,匿名方法產生的委派實例相等性遵循特定規則。當兩個委派實例源自語義相同的匿名方法表達式,且捕獲相同的外部變數集合時,它們可能相等(但不強制)。反之,若匿名方法語義不同或捕獲的外部變數集合不同,則絕不相等。這項規則同時適用於相等運算子(§7.9.8)與 object.Equals 方法。
匿名方法參數的明確賦值狀態與具名方法一致:
外部變數的明確賦值狀態會從匿名方法表達式的上下文繼承而來。在匿名方法區塊內,明確賦值推導規則與一般區塊相同(§5.3.3)。匿名方法表達式結束後,外部變數的賦值狀態回歸至表達式之前的狀態。
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);
}
C# 2.0 引入從方法群組(§7.1)到相容委派類型的隱式轉換。若允許 new D(E) 形式的委派建立表達式,則存在從 E 到 D 的隱式轉換,結果等價於 new D(E)。
// C# 1.0 風格
okButton.Click += new EventHandler(OkClick);
cancelButton.Click += new EventHandler(CancelClick);
// C# 2.0 簡化語法
okButton.Click += OkClick;
cancelButton.Click += CancelClick;
對於其他隱式/顯式轉換,仍可使用轉換運算子:
object obj = (EventHandler)myDialog.OkClick;
方法群組與匿名方法表達式會影響多載解析,但不參與型別推斷(詳細請見 §20.6.4)。
本節展示 Microsoft C# 編譯器處理匿名方法的內部機制(非強制性實作)。假設委派類型:public delegate void D();
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");
}
}
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);
}
}
class Test {
void F() {
int y = 123;
D d = delegate { Console.WriteLine(y); };
}
}
區域變數「提升」為編譯器產生類別的欄位:
class Test {
void F() {
__locals1 = new __Locals1();
__locals1.y = 123;
D d = new D(__locals1.__Method1);
}
class __Locals1 {
public int y;
public void __Method1() {
Console.WriteLine(y);
}
}
}
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); };
}
}
}
編譯器為每個語句區塊產生巢狀類別:
class Test {
void F() {
__locals1 = new __Locals1();
__locals1.__this = this;
__locals1.y = 123;
for (int i = 0; i < 10; i++) {
__locals2 = new __Locals2();
__locals2.__locals1 = __locals1;
__locals2.z = i * 2;
D d = new D(__locals2.__Method1);
}
}
class __Locals1 {
public Test __this;
public int y;
}
class __Locals2 {
public __Locals1 __locals1;
public int z;
public void __Method1() {
Console.WriteLine(__locals1.__this.x + __locals1.y + z);
}
}
}
透過這層巢狀結構,匿名方法能存取所有被捕獲的變數,且各變數生命週期正確獨立。