局部变量捕获
局部函数,匿名函数,可以直接在函数体内使用局部变量。
如果作为委托使用,传递给别人。可能在别人使用委托的时候,这些局部变量的作用域就已经消失了。
因此,编译器会对这些变量做出特殊的操作,将这些变量封装在一个类里面(编译器自己创建的类)。
在原变量出了作用域后,因为在封装类里面还有引用,不会被清除。
不可捕获的变量
引用变量(ref变量,in参数,out参数),和ref结构不允许被捕获。
引用捕获
委托捕获的变量是以引用传递的方式使用的。
也就是说,如果使用和捕获之间修改了捕获的变量,那么使用时是修改后的结果。
Action act =null;
for (int i = 0; i < 10; i++)
{
act += () => Console.WriteLine(i);
}
act();//输出结果全是10
如果希望使用声明委托当时的值,只需要另外声明一个局部变量进行复制操作。
引用类型的复制可能需要使用深拷贝。
Action act =null;
for (int i = 0; i < 10; i++)
{
int j=i;
act += () => Console.WriteLine(j);
}
act();//输出0到9
附注
foreach循环和for循环不同。
foreach循环是一个语法糖,在编译后不会呈现出foreach循环,而是while循环。
然后,在foreach循环中生命的变量,在编译后,是在while循环内声明的。
也就是说foreach循环中捕获item,不会出现上述for循环的问题。
int[] i = { 1, 2, 3, 4, 5 };
Action action = null;
foreach (var item in i)
{
action += () => Console.WriteLine(item);
}
action();//确实是1,2,3,4,5