C# 闭包中的Foreach变量。为什么这些片段的结果不同?
有人能解释一下为什么这段话:C# 闭包中的Foreach变量。为什么这些片段的结果不同?,c#,.net,multithreading,task,C#,.net,Multithreading,Task,有人能解释一下为什么这段话: // Create required tasks foreach (var messageToSend in messagesToSend) { EmailMessage messageToBeSent = messageToSend; Task<bool> processingTask = new Task<bool>(() => SendMessage(messageToBeSent)); processingTask.
// Create required tasks
foreach (var messageToSend in messagesToSend)
{
EmailMessage messageToBeSent = messageToSend;
Task<bool> processingTask = new Task<bool>(() => SendMessage(messageToBeSent));
processingTask.Start();
}
//创建所需的任务
foreach(变量messageToSend in messagesToSend)
{
EmailMessageToSent=messageToSend;
任务处理任务=新任务(()=>SendMessage(messageToBeSent));
processingTask.Start();
}
工作原理与此不同:
// Create required tasks
foreach (var messageToSend in messagesToSend)
{
Task<bool> processingTask = new Task<bool>(() => SendMessage(messageToSend));
processingTask.Start();
}
//创建所需的任务
foreach(变量messageToSend in messagesToSend)
{
任务处理任务=新任务(()=>SendMessage(messageToSend));
processingTask.Start();
}
在第一个代码段中,所有任务都以自己的消息开头,而在第二个代码段中,所有任务都以相同的消息开头
Resharper给出了这样的描述:“访问闭包中的foreach变量。使用不同版本的编译器编译时可能会有不同的行为。”为什么会有不同的行为?您的第二个代码示例有一个单个
,所有lambda表达式都在它们的闭包中捕获
代理运行时,将使用变量的当前值
Resharper给出了这样的描述:“访问闭包中的foreach变量。使用不同版本的编译器编译时,可能会有不同的行为。”为什么会有不同的行为
C#4和C#5之间有一个突破性的变化,这是因为foreach中的循环变量受到闭包的影响,特别是自从在C#3中引入lambda表达式以来。Resharper警告您这一点,以防您可能依赖于或以其他方式期待前面的语义
最快的结果是,在C#4中,循环变量在循环的每个迭代之间共享,闭包捕获变量,因此当大多数人关闭循环变量时,这会导致意外的结果
在C#5中,循环的每个迭代都有自己的变量,因此一个迭代中的闭包不会像其他迭代一样关闭同一个变量,从而导致更多的预期结果(对大多数人来说)
这让我们找到了你问题的核心:
在第一个代码段中,所有任务都以自己的消息开头,而在第二个代码段中,所有任务都以相同的消息开头
在第一个代码段中,您正在循环中创建循环变量的副本,并且闭包发生在内部变量上。在第二种情况下,直接关闭循环变量。假设您是在C#4下运行的,因此前面的语义适用。如果在C#5中运行,两个版本的循环输出应该是一致的。这就是Resharper所指的更改,它还应该让您了解如何用C#4构造代码(即,使用您编写的第一个版本)
正如Justin Pihony在评论中指出的那样,他写了一篇关于前一种语义学的文章,其中也提到了C#5的变化。关于这个主题的问题每天都会在这里被问几次……试着对这个主题做一些研究,或者只是看看“相关的”谢谢你的简短回答,它解释了埃里克·利珀特所说的一切:有人能解释“C4”和“C5”是什么意思吗?我从来没听说过这个数字。它与.NET Framework 3.5、4.0和4.5有关吗?@RandyGamage