【问题标题】:Unexpected behaviour when using foreach and lambdas [duplicate]使用 foreach 和 lambdas 时的意外行为 [重复]
【发布时间】:2013-09-25 16:48:54
【问题描述】:

我有以下代码...

namespace ConsoleApplication2
{
    public delegate void Task();

    class Scheduler
    {
        private List<Task> tasks = new List<Task>();

        public void AddTask(Task myTask)
        {
            tasks.Add(myTask);
        }

        public void Start()
        {
            foreach (var task in tasks)
                task();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {            
            var list = new List<string>() { "A", "B", "C" };
            var scheduler = new Scheduler();
            foreach (var item in list)
            {
                scheduler.AddTask(() => { Console.WriteLine(item); });
            }
            scheduler.Start();
        }
    }
}

输出是……

C
C
C

但是,如果我将 Main 方法的 foreach 部分更改为:

    foreach (var item in list)
    {
        var i = item;
        scheduler.AddTask(() => { Console.WriteLine(i); });
    }

我得到以下输出:

A
B
C

我的愚蠢假设是两个程序都应该生成相同的输出。有人可以解释一下为什么会这样吗?

【问题讨论】:

  • 阅读 Eric Lippert 的博文:blogs.msdn.com/b/ericlippert/archive/2009/11/12/…
  • 请注意,这在 C# 5 中已“修复”,将不再发生。您将从任一选项中获得第二个输出。
  • 这不仅适用于 lambda,也适用于一般的委托闭包。
  • @ReedCopsey 这仅适用于 foreach 循环,适用于其他类型的循环,例如for(int i = 0; i &lt; 10; i++) 你还需要使用临时变量。
  • @csharpfolk 是的 - 但是这个具体的例子是固定的;)

标签: c# foreach lambda


【解决方案1】:

在您的循环的第一个版本中,当调度程序执行 Console.Writeline() 时,item 的值正在发生变化 - item 是一个局部变量,当三个任务正在执行时,其值为“C”。

在第二个版本中,您正在创建对 item 的引用,然后在任务中使用它,因此它可以正确显示当时的值。

如果您安装了 ReSharper,它会警告您“访问修改后的闭包”:这是他们对原因的解释:

http://confluence.jetbrains.com/display/ReSharper/Access+to+modified+closure

【讨论】:

    猜你喜欢
    • 2013-01-28
    • 2017-05-19
    • 2011-09-11
    • 1970-01-01
    • 1970-01-01
    • 2018-04-15
    • 1970-01-01
    • 1970-01-01
    • 2014-09-25
    相关资源
    最近更新 更多