匿名方法的局部變數用法
到現在為止,我們對匿名方法如何工作以及內部如何?有了一點基本的理解。從根本上說,C#建立了private方法來封裝匿名方法。同時這些方法的簽名與它們被分配到的委託相匹配。現在,讓我們看看下面的代碼:
public class Program { public delegate void MyDelegate(); public static void Main(string[] args) { int iTemp = 100; MyDelegate dlg = delegate { Console.WriteLine(iTemp); }; dlg(); } } |
對於我們到現在為止對匿名方法已瞭解的內容來說,這段代碼不應該編譯。因為我們沒有使用如何執行個體資料成員,C#編譯器應該在''Program''類中建立一個private靜態方法來封裝這個匿名方法。但是新的方法如何訪問局部變數呢?這讓我們相信該代碼將不能被編譯。但是令人驚訝的是,C#編譯器成功編譯了這個代碼而沒有任何錯誤或警示。而且,當你執行這個樣本時,在控制台螢幕上輸出列印出iTemp變數的正確的值。現在讓我們進入匿名方法的進階話題。一個匿名方法有封裝在其方法體中使用了的環境變數的值的能力。這個封裝應用於匿名方法被定義的方法中的所有局部變數。當C#編譯器在一個匿名方法的方法體中識別出用到一個局部變數,它就會做如下事情:
1. 建立一個新的private類作為匿名方法被定義的類的一個內部類。
2. 在新類(譯註:即內部類)中建立一個公用資料成員,使用與用在匿名方法體中的局部變數相同的類型和名稱。
3. 在封裝匿名方法的新類中建立一個public執行個體方法。
4. 用新類中的聲明替代局部變數的聲明。建立該新類的一個執行個體代替局部變數的聲明。
5. 用新類執行個體的資料成員替代在匿名方法體內部和外部使用的局部變數。
6. 用在新類中定義的執行個體方法的地址取代匿名方法的定義。
因此在編譯時間,上面的代碼將被C#編譯器翻譯為如下代碼:
public class Program { private class InnerClass { private void InstanceMethod() { Console.WriteLine(iTemp); } public int iTemp; } public delegate void MyDelegate(); public static void Main(string[] args) { InnerClass localObject = new InnerClass(); localObject.iTemp = 100; MyDelegate dlg = new MyDelegate(localObject.InstanceMethod); dlg(); } } |
正如上面的虛擬碼所示,C#編譯器為''Program''類產生了一個private內部類。在匿名方法中使用的局部變數作為新的已建立的內部類的一個執行個體資料成員而捕獲。並且匿名方法本身被封裝在內部類的執行個體方法中。最後,該執行個體方法在Main方法中作為一個委託處理器而使用。這樣,當委託被調用時,對於在被封裝入匿名方法中的局部變數將會有一個正確的值。下面圖中選定的部分顯示了由C#編譯器默默添加到''Program'' 類的新的private內部類。
被用在匿名方法中的局部變數有著超出用到它們的外部常規方法的生命週期。這個技術,在其它語言中,就是大家都知道的closures。除去匿名方法提供的簡單文法,closures是匿名方法提供給開發人員的一個功能強大的技術。該技術允許委託處理器代碼(匿名方法)訪問在常規方法內部被定義的局部變數。這就允許out-of-band資料,除了委託參數之外還有資料將被傳遞到委託,以供在其方法執行時使用。沒有這個技術,每個委託和其相應的處理器方法就不得不聲明表示局部上下文資料的參數,隨著時間的過去這(譯註:指不斷聲明表示局部上下文資料的參數)將變得難於管理。