在闭包中Foreach变量.为什么这些片段的结果不同?
本文关键字:片段 结果 闭包 Foreach 变量 为什么 | 更新日期: 2023-09-27 18:07:54
谁能解释一下为什么这段代码:
// Create required tasks
foreach (var messageToSend in messagesToSend)
{
EmailMessage messageToBeSent = messageToSend;
Task<bool> processingTask = new Task<bool>(() => SendMessage(messageToBeSent));
processingTask.Start();
}
与下面的不同:
// Create required tasks
foreach (var messageToSend in messagesToSend)
{
Task<bool> processingTask = new Task<bool>(() => SendMessage(messageToSend));
processingTask.Start();
}
在第一个片段中,所有任务都以自己的消息开始,而在第二个片段中,所有任务都以相同的消息开始?
Resharper给出了这样的描述:"访问闭包中的foreach变量。使用不同版本的编译器编译时可能会有不同的行为。为什么它会有不同的行为?
Resharper给出了这样的描述:"访问闭包中的foreach变量。使用不同版本的编译器编译时可能会有不同的行为。为什么它会有不同的行为?
在c# 4和c# 5之间有一个重大的变化,因为foreach中的循环变量受到闭包的影响,特别是在c# 3引入lambda表达式之后。Resharper警告你这一点,以防你可能依赖于或以其他方式期望使用前一种语义。
在c# 4中,循环变量在每次循环迭代之间共享,闭包捕获该变量,因此当大多数人关闭循环变量时,会导致意想不到的结果。
在c# 5中,循环的每次迭代都有自己的变量,因此一次迭代中的闭包不会像其他迭代那样关闭相同的变量,从而导致更多预期的结果(对大多数人来说)。
这让我们找到了问题的核心:
在第一个片段中,所有任务都以自己的消息开始,而在第二个片段中,所有任务都以相同的消息开始?
在第一个代码片段中,您在循环中创建了循环变量的副本,并且闭包发生在内部变量上。在第二种情况下,直接关闭循环变量。假设您在c# 4下运行,因此适用前一种语义。如果在c# 5中运行,两个版本的循环输出应该是一致的。这就是Resharper所指的改变,它也应该让你理解如何在c# 4中构建你的代码(也就是说,使用你写的第一个版本)。
正如Justin Pihony在评论中指出的那样,Eric Lippert写了一篇非常有用的关于前语义的博客文章,其中也暗示了c# 5的变化。
第二个代码示例有一个单个 messageToSend
,所有lambda表达式都在它们的闭包中捕获它。
当委托运行时,它们使用变量的当前值