反编译分析C#闭包
一、问题描述:
比如有这样的代码:
它的输出结果是 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开销。