C# 2.0 引入匿名方法,允許在委托預期的位置直接內聯代碼塊。本文依據語言規範(第 21 章),系統梳理匿名方法的語法結構、簽名規則、隱式轉換條件、委托創建表達式、方法體限制、外部變量捕獲機制、實例化語義及運行時計算行為。
語法形式:
delegate anonymous-method-signature_opt block
anonymous-method-signature 為可選的參數列表 (parameter-list_opt)。parameter-modifier_opt type identifier(可含 ref/out)。params 不影響兼容性。out 參數。匿名方法表達式 A 與委托類型 D 兼容的條件:
A 無簽名 → D 可有零或多參數,但無 out 參數。A 有簽名 → D 必須參數數量、類型、ref/out 修飾符逐一對應。D 為 void → A 中 return 不得帶表達式。D 為類型 R → A 中所有 return 表達式需隱式轉換至 R,且塊結束點不可達。除到兼容委托類型的隱式轉換外,匿名方法無其他轉換(甚至不可轉為 object)。
delegate void D(int x);
D d1 = delegate { }; // OK:無簽名匹配無 out 參數委托
D d2 = delegate() { }; // 錯誤:有空簽名但委托有參數
D d4 = delegate(int x) { }; // OK:簽名完全匹配
D d6 = delegate(int x) { return x; }; // 錯誤:void 返回類型不匹配
delegate void E(out int x);
E e1 = delegate { }; // 錯誤:委托有 out 參數
E e2 = delegate(out int x){x=1;} // OK
delegate int P(params int[] a);
P p3 = delegate { return 1; }; // OK:塊結束點不可達
P p5 = delegate(int[] a){ return a[0]; }; // OK:params 不影響兼容性
new D(delegate { ... }) 等價於 (D)delegate { ... },同樣遵循上述隱式轉換規則。
ref/out 參數。this(顯式或隱式)。goto/break/continue 跳出塊或進入巢狀匿名方法。return 從最近封閉匿名方法返回,而非外層函數成員。__Method1)。作用域包含匿名方法表達式的局部變量、值參數、參數陣列,以及實例函數成員中的 this(視為值參數)。
當外部變量在匿名方法中被引用,即被「捕獲」。捕獲變量的生命週期至少延長至引用該匿名方法的委托可被垃圾回收為止。
xdelegate int D();
class Test {
static D F() {
int x = 0;
D result = delegate { return ++x; };
return result;
}
static void Main() {
D d = F();
Console.WriteLine(d()); // 1
Console.WriteLine(d()); // 2
Console.WriteLine(d()); // 3
}
}
三次調用操作同一 x 實例,輸出 1、2、3。
局部變量在進入作用域時實例化。若在循環中聲明,每次迭代產生新實例;若移至循環外,則共用單一實例。
static D[] F() {
D[] result = new D[3];
for (int i = 0; i < 3; i++) {
int x = i * 2 + 1;
result[i] = delegate { Console.WriteLine(x); };
}
return result;
}
// 輸出:1, 3, 5
static D[] F() {
D[] result = new D[3];
int x;
for (int i = 0; i < 3; i++) {
x = i * 2 + 1;
result[i] = delegate { Console.WriteLine(x); };
}
return result;
}
// 輸出:5, 5, 5
x、獨立 ystatic D[] F() {
D[] result = new D[3];
int x = 0;
for (int i = 0; i < 3; i++) {
int y = 0;
result[i] = delegate { Console.WriteLine("{0} {1}", ++x, ++y); };
}
return result;
}
// 輸出:
// 1 1
// 2 1
// 3 1
int x = 0;
Setter s = delegate(int value) { x = value; };
Getter g = delegate { return x; };
s(5); Console.WriteLine(g()); // 5
s(10); Console.WriteLine(g()); // 10
null、this 或其他)。delegate double Function(double x);
class Test {
static double[] Apply(double[] a, Function f) { ... }
static void F(double[] a, double[] b) {
a = Apply(a, delegate(double x) { return Math.Sin(x); });
b = Apply(b, delegate(double y) { return Math.Sin(y); });
// 兩匿名方法語義相同、捕獲集合均為空 → 編譯器可使其共用委托實例
}
}
C# 2.0 匿名方法規範完整定義了語法、類型轉換、變量捕獲與生命週期、運行時語義。掌握這些細節,能幫助開發者正確理解閉包行為、避免常見陷阱(如循環中變量捕獲),並為後續 Lambda 表達式與 LINQ 的學習奠定堅實基礎。