我在C#中有以下代码片段:
var actions = new List<Func<int>>();
IEnumerable<int> values = new List<int> { 1, 2, 3 };
foreach (int value in values)
{
actions.Add(() => value * value);
}
foreach (var action in actions)
{
Console.WriteLine(action()); ;
}
Console.ReadLine();
它运行正常,但我没有得到我期望的结果。
实际结果
9,9,9
预期结果
1,4,9
为什么我没有得到我期望的结果?
答案 0 :(得分:13)
您需要捕获循环内的变量。现在,您的延迟执行操作正在使用第一个value
循环中的foreach
的最后一个值。
var actions = new List<Func<int>>();
IEnumerable<int> values = new List<int> { 1, 2, 3 };
foreach (int value in values)
{
var v = value;
actions.Add(() => v * v);
}
foreach (var action in actions)
{
Console.WriteLine(action()); ;
}
Console.ReadLine();
请注意var v = value;
行。
答案 1 :(得分:12)
您正在捕获lambda表达式中的循环变量,这意味着最终调用委托时,它会看到循环变量的 final 值。
简单修复:
foreach (int value in values)
{
int copy = value;
actions.Add(() => copy * copy);
}
这样在循环的每次迭代中都会得到一个新的copy
变量,因此每个委托表达式将捕获一个不同的变量,并且它们不受循环变量(value
)变化的影响随着时间的推移。
Eric Lippert在"Closing over the loop variable considered harmful"(以及part two)中解释了这一点。
基本上这是C#中的“陷阱”,几乎每个人都迟早会失败。