我有这段代码:
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++;
}
...将始终使用i
和tile
变量的最后值执行委托。为什么会发生这种情况,为什么我必须制作这些变量的本地副本,尤其是非引用类型int i
?
答案 0 :(得分:3)
已知“问题”,请查看Eric的博客Closures, captured variables.
Microsof决定进行彻底改变,并在C#5中修复它。
答案 1 :(得分:2)
这是预期的:当你创建一个lambda时,编译器会创建一个闭包。它将捕获临时变量的值,但它不会捕获循环变量的值以及在创建lambda之后发生变化的其他变量。
问题的核心是委托创建和执行时间不同。循环运行时,委托对象是创建的,但在循环完成后,被称为。在调用委托时,循环变量具有循环完成时达到的值,从而产生您看到的效果(值不会更改,您可以看到循环中的最后一个值)。
忘记创建一个临时变量以便在闭包中使用是一个很常见的错误,流行的代码分析器(例如ReSharper)会对此发出警告。
答案 2 :(得分:2)
您不能使用这样的循环变量,因为在执行委托时,循环变量可能处于其最终(循环结束)状态,因为它在执行删除时使用变量的值,而不是创建
您需要制作变量的本地副本才能使其正常工作:
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++;
}
通过在每个循环上创建本地副本,该值不会更改,因此您的委托将使用您期望的值。