Action委托使用在foreach循环外声明的变量的最后值

我有这段代码:

int i = 0; foreach(var tile in lib.dic.Values) { var ii = i; var t = tile; Button b = new Button( () = > { MainStatic.tile = t; } ); Checkbox c = new Checkbox( () = > { lib.arr[ii].b = !lib.arr[ii].b; } ); i++; } 

虽然上面的代码可以正常工作,但下面的代码如下:

 int i = 0; foreach(var tile in lib.dic.Values) { Button b = new Button( () = > { MainStatic.tile = tile; } ); Checkbox c = new Checkbox( () = > { lib.arr[i].b = !lib.arr[i].b; } ); i++; } 

…将始终使用itile变量的最后值执行委托。 为什么会发生这种情况,为什么我必须制作这些变量的本地副本,尤其是非引用类型int i

已知“问题”,请查看Eric的博客Closures,捕获变量。

Microsof决定进行一次重大改变,并在C#5中修复它。

您不能使用这样的循环变量,因为在执行委托时,循环变量可能处于其最终(循环结束)状态,因为它在执行删除时使用变量的值,而不是创建。

您需要制作变量的本地副本才能使其生效:

 int i = 0; foreach(var tile in lib.dic.Values) { var tileForClosure = tile; var iForClosure = i; Button b = new Button( () = > { MainStatic.tile = tileForClosure ; } ); Checkbox c = new Checkbox( () = > { lib.arr[iForClosure].b = !lib.arr[iForClosure].b; } ); i++; } 

通过在每个循环上创建本地副本,该值不会更改,因此您的委托将使用您期望的值。

这是预期的:当你创建一个lambda时,编译器会创建一个闭包 。 它将捕获临时变量的值,但它不会捕获循环变量的值以及在创建lambda之后更改的其他变量。

问题的核心是委托创建和执行时间不同。 在循环运行时创建委托对象,但在循环完成后调用它。 在调用委托时,循环变量具有循环完成时达到的值,从而产生您看到的效果(值不会更改,您可以看到循环中的最后一个值)。

忘记创建一个用于闭包的临时变量是一个常见的错误,流行的代码分析器(例如ReSharper)会警告你。