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

Visual C# 2.0 匿名方法深度解析:編譯器內部實作與閉包機制

     編輯:關於C語言

匿名方法基礎

C# 2.0 引入匿名方法,允許以內聯程式碼區塊建立委派實例,省去額外方法定義,減少類別雜訊。語法:delegate(參數) { 主體; }。

範例:字串集合過濾


// 傳統寫法:需額外定義 FilterStringWithA、FilterStringWithT 方法
string[] aStrings = col.GetFilteredItemArray(FilterStringWithA);

// 匿名方法:內聯定義過濾條件
string[] aStrings = col.GetFilteredItemArray(delegate(string s) {
    return s[0] == 'A';
});

編譯器內部實作機制

CLR 無法直接執行「自由流動」的程式碼區塊,每個方法必須屬於某個型別,且為靜態或實例方法。C# 編譯器會自動在定義匿名方法的類別中產生私有靜態/實例方法,並以此方法位址取代匿名方法。

情境 1:未使用任何外部變數 → 產生 static 方法


// 原始碼
delegate { Console.WriteLine("Hello"); };

// 編譯器產生
private static void __AnonymousMethod$001() { Console.WriteLine("Hello"); }

ILDasm 可見新增的 private static 方法。

情境 2:使用類別的靜態欄位 → 仍產生 static 方法

靜態方法即可存取靜態資料,效率較高。

情境 3:使用類別的實例欄位 → 產生 instance 方法


public class Program {
    int count = 0;
    void Test() {
        Action d = delegate { Console.WriteLine(++count); };
        d();
    }
}

// 編譯器產生
private void __AnonymousMethod$002() { Console.WriteLine(++this.count); }

必須為實例方法才能透過 this 存取實例欄位。

閉包:捕獲區域變數

當匿名方法引用定義它的方法中的區域變數時,編譯器會產生一個私有巢狀類別,將該區域變數「提升」為該類別的公用欄位,並將匿名方法包裝為該類別的實例方法。

簡單捕獲範例


// 原始碼
int temp = 100;
Action d = delegate { Console.WriteLine(temp); };
d();

// 編譯器轉換後等效程式碼
class __ClosureClass {
    public int temp;
    public void __Method() { Console.WriteLine(temp); }
}

__ClosureClass loc = new __ClosureClass();
loc.temp = 100;
Action d = new Action(loc.__Method);
d();

區域變數 temp 的生命週期延長至委派存活期間,這就是閉包。

巢狀作用域捕獲

外層作用域變數 → 產生第一個巢狀類別;內層作用域變數 + 匿名方法 → 產生第二個巢狀類別,並持有對第一個類別實例的參考。

迴圈中捕獲迴圈變數的經典陷阱


Action d = null;
for (int i = 1; i <= 5; i++) {
    Action tempD = delegate { Console.WriteLine(i); };
    d += tempD;
}
d(); // 預期 1 2 3 4 5,實際輸出 6 6 6 6 6

原因:i 被提升至單一閉包類別實例的欄位,迴圈結束時 i=6,所有委派共享同一實例,故皆印出 6。

修正:在迴圈內建立區域複本


for (int i = 1; i <= 5; i++) {
    int k = i; // 每次迭代新建區域變數
    Action tempD = delegate { Console.WriteLine(k); };
    d += tempD;
}
// 輸出 1 2 3 4 5

每次迭代產生獨立閉包實例,捕獲當下的 k 值。

總結

  • 匿名方法是語法糖,編譯器自動產生私有方法(靜態/實例)或巢狀類別(閉包)。
  • 理解捕獲機制對避免迴圈陷阱、管理變數生命週期至關重要。
  • C# 3.0+ Lambda 表達式(() => ...)本質相同,語法更簡潔。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved