考慮以下代碼片段:
using System;
public delegate void D();
public class Alpha {
public virtual void Blah() {
Console.WriteLine("Alpha.Blah");
}
}
public class Bravo : Alpha {
public override void Blah() {
Console.WriteLine("Bravo.Blah");
base.Blah();
}
public void CharlIE() {
int x = 123;
D d = delegate {
this.Blah();
base.Blah();
Console.WriteLine(x);
};
d();
}
}
class Program {
public static void Main(string[] args) { }
}
使用 .NET Framework 3.5 Beta 2 的 C# 編譯器(csc.exe)編譯上述代碼,會得到警告:
test1.cs(23,13): warning CS1911: 從匿名方法、lambda表達式、查詢表達式或迭代器通過“base”關鍵字訪問成員“Alpha.Blah()”會導致代碼無法驗證。請考慮將這種訪問移入針對包含類型的輔助方法中。
.NET Framework 3.5 RTM 版本結果相同。而 Mono 1.2.5.1 則完全不報錯。
CharlIE() 方法中使用 this/base 存取自身/基類成員,看似正常。問題出在 C# 處理閉包生成代碼的方式。
C# 2.0 引入匿名委派,C# 3.0 引入 Lambda 運算式,兩者都允許定義巢狀方法。巢狀方法遵循詞法作用域,可存取外部包圍作用域的變數,包括外部的 this,形成「閉包」。
當巢狀方法實例化後,其生命週期可能超過外部方法。它從外部環境「捕獲」的變數,彷彿從外部「逃逸」出來。範例中,x 和 this 都成為逃逸變數。
這些逃逸變數必須與巢狀方法生命週期同步,即使外部方法已返回也不得銷毀,因此不能配置在堆疊上,必須在堆上配置記憶體。編譯器的做法是建立私有巢狀類別來保存這些逃逸變數,並將匿名委派轉為該類別的方法。