封闭中的Foreach变量.为什么这些片段的结果不同?

Iva*_* P. 8 .net c# multithreading task

任何人都可以解释为什么这个片段:

// Create required tasks
foreach (var messageToSend in messagesToSend)
{
  EmailMessage messageToBeSent = messageToSend;
  Task<bool> processingTask = new Task<bool>(() => SendMessage(messageToBeSent));
  processingTask.Start();
}
Run Code Online (Sandbox Code Playgroud)

与此不同的是:

// Create required tasks
foreach (var messageToSend in messagesToSend)
{
  Task<bool> processingTask = new Task<bool>(() => SendMessage(messageToSend));
  processingTask.Start();
}
Run Code Online (Sandbox Code Playgroud)

在第一个片段中,所有任务都以自己的消息开头,而在第二个片段中,所有任务都以相同的消息开头?

Resharper给出了这样的描述:"在关闭时访问foreach变量.使用不同版本的编译器编译时可能会有不同的行为." 为什么它会有不同的行为?

Ant*_*ram 16

Resharper给出了这样的描述:"在关闭时访问foreach变量.使用不同版本的编译器编译时可能会有不同的行为." 为什么它会有不同的行为?

有C#4和C#5之间的重大更改是由于的foreach循环变量被封锁影响的方式,特别是因为在C#中引入lambda表达式3 ReSharper的是警告你的这个,如果你可能依赖或以其他方式已经开始期待前一种语义.

快速结果是在C#4中,循环变量在循环的每次迭代之间共享,并且闭包捕获变量,因此当它们关闭循环变量时,它导致大多数人意外的结果.

在C#5中,循环的每次迭代都有自己的变量,因此一次迭代中的闭包不会在与其他迭代相同的变量上关闭,从而导致更多的预期结果(对于大多数人来说).

这让我们了解你的问题的核心:

在第一个片段中,所有任务都以自己的消息开头,而在第二个片段中,所有任务都以相同的消息开头?

在第一个片段中,您将在循环内创建循环变量的副本,并且闭包发生在内部变量上.在第二个中,您直接关闭循环变量.据推测,您在C#4下运行,因此前一种语义适用.如果在C#5中运行,两个版本的循环输出应该是一致的.这是Resharper所引用的更改,它还应该让您了解如何在C#4中构建代码(即,使用您编写的第一个版本).

正如Justin Pihony在评论中指出的那样,Eric Lippert撰写了一篇关于前语义的非常有用的博客文章,该文章也提到了C#5的变化.

  • 要调用Eric Lippert:http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx (4认同)