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

C# 2.0 規範深度解析:匿名方法進階特性(下)

     編輯:關於C語言

委派實例相等性比較

在 C# 2.0 中,匿名方法產生的委派實例相等性遵循特定規則。當兩個委派實例源自語義相同的匿名方法表達式,且捕獲相同的外部變數集合時,它們可能相等(但不強制)。反之,若匿名方法語義不同或捕獲的外部變數集合不同,則絕不相等。這項規則同時適用於相等運算子(§7.9.8)與 object.Equals 方法。

明確賦值規則

匿名方法參數的明確賦值狀態與具名方法一致:

  • ref/value 參數:視為已明確賦值
  • out 參數:需在方法正常返回前明確賦值(§5.1.6)

外部變數的明確賦值狀態會從匿名方法表達式的上下文繼承而來。在匿名方法區塊內,明確賦值推導規則與一般區塊相同(§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();

案例 1:無捕獲外部變數

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");
    }
}

案例 2:捕獲 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);
    }
}

案例 3:捕獲區域變數(變數提升)

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);
        }
    }
}

案例 4:捕獲 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); };
        }
    }
}

編譯器為每個語句區塊產生巢狀類別:

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);
        }
    }
}

透過這層巢狀結構,匿名方法能存取所有被捕獲的變數,且各變數生命週期正確獨立。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved