如果我們仔細回憶我們關於匿名方法的內部工作機制的知識,我提到:在匿名方法中被捕獲的任何局部變量將會被該作用域的一個新的已創建內部類的實例數據成員替代。對於循環控制變量,作用域是包含了for循環的作用域,這就是上面的簡單代碼所示的main方法體。因此當該代碼編譯時,C#編譯器生成創建了內部類的實例的代碼,包裝了匿名方法和循環計數變量,在for循環的外部。並且該內部類的實例的數據成員,代表了循環計數變量,將被用來替代用於for循環而且也在匿名方法中使用的原始循環計數變量。因此來自內部類的相同實例的數據成員被用於for循環並且也用在包裝匿名方法的實例方法中。作為循環完成時的結果,實例數據成員會增加六次。這裡有一個需要注意的重要地方:盡管這個循環在五次迭代後結束,在它跳出循環控制結構時循環計數變量被增加了六次。既然該循環控制變量是一個實例數據成員,第六次增加觸發了已由循環計數變量提供的循環結束條件。既然相同實例的一個方法被用做匿名方法的委托處理器,在委托結束時被調用,所有委托的實例將被指向相同實例,同時將為數據成員顯示相同值,就是6。這就是我在本節開始已提到過的有危險影響的一面。
為了克服這個問題並獲得預期的結果,匿名方法應該在for循環的作用域中捕獲一個局部變量,它將有與循環計數變量的相同的值。這可以通過如下修改示例代碼獲得:
public class Program
{
public delegate void MyDelegate();
public static void Main(string[] args)
{
MyDelegate d = null;
for (int i = 1; i <= 5; i++)
{
int k = i;
MyDelegate tempD = delegate
{
Console.WriteLine(k);
};
d += tempD;
}
d();
}
}
在你運行上面的代碼示例時,將會獲得預期的輸出,也就是:
1
2
3
4
5
原因就是,C#編譯器將為for循環的每次迭代而包裝局部變量''k''的內部類創建 實例。同時包裝了每個循環迭代的實例上的匿名方法的這個方法被用做一個委托處理器。
總結
匿名方法是C#2.0語言增加的一個非常有用和強大的功能。除了介紹的一些對委托聲明和用法上的語法改進,Microsoft已在使匿名方法代碼自然融入所包含的方法體方面獲得很大進展,包括訪問在包含(匿名方法)的方法定義的作用域中的局部變量。最後,我希望本文提供給C#開發人員正確而聰明地利用匿名方法的必備知識。