调用Func <int>时为什么会出错?

san*_*ngh 6 c# lambda

我在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();
Run Code Online (Sandbox Code Playgroud)

它运行正常,但我没有得到我期望的结果.

实际结果

9,9,9

预期结果

1,4,9

为什么我没有得到我期望的结果?

Eni*_*ity 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();
Run Code Online (Sandbox Code Playgroud)

注意这一var v = value;行.


Jon*_*eet 12

您正在捕获lambda表达式中的循环变量,这意味着当最终调用委托时,它会看到循环变量的最终值.

简单修复:

foreach (int value in values)
{
    int copy = value;
    actions.Add(() => copy * copy);
}
Run Code Online (Sandbox Code Playgroud)

这样您就可以copy在循环的每次迭代中获得一个新变量,因此每个委托表达式将捕获一个不同的变量,并且它们不会受循环变量(value)随时间变化的影响.

Eric Lippert在"关闭循环变量被认为有害"(以及第二部分)中对此做了很好的解释.

基本上这是C#中的"陷阱",几乎每个人都迟早会失败.

  • @geek:使用匿名函数(匿名方法或lambda表达式)创建的任何委托 - 它是关于如何捕获变量,而不是关于泛型. (2认同)