一、問題描述:
比如有這樣的代碼:
它的輸出結果是 3,3,3。
通過搜索得知這一現象是因為C#閉包導致的.
我們借助ILSpy看下IL中間代碼,首先它生成了一個名叫DisplayClass的類,類中定義了i的字段
主代碼:
在for循環外,定義了DisplayClass的實例,并且對DisplayClass.i進行賦值
其次看下i++實現,是針對DisplayClass.i進行了自增操作。
我們可以對這段代碼再進行AI翻譯,所以最終執行輸出的a.i是最新值3
var actions = new List<Action>();
DisplayClass a=new DisplayClass();
a.i=0;
while(a.i<3){actions.Add(() => Console.WriteLine(a.i));a.i=a.i+1;
}
二、解決方式
解決閉包也很簡單,就是用局部變量進行賦值
我們再反編譯分析下原理
它是在for循環里面生成的DisplayClass的實例,相當于每循環一次都會生成一次實例,那么匿名函數持有的是局部變量DisplayClass實例,所以就能輸出正確的值。
總結:
匿名函數引用外部變量i,會在定義變量i的時候實例化一個DisplayClass類,類中聲明了i的字段。
1.如果是引用的循環體外的變量,那么每個匿名函數都持有的同一個DisplayClass實例,會導致輸出的結果都是最新的值。
2.如果是引用的循環體內的變量,那么每個匿名函數都持有與之對應的DisplayClass實例,能保證值輸出正確,但也會帶來相應的GC開銷。