我有一段代码,我认为因为封闭而会起作用;但是,结果证明不是这样。这里发生了什么不能产生预期的输出(每个单词中的一个)?
代码:
string[] source = new string[] {"this", "that", "other"};
List<Thread> testThreads = new List<Thread>();
foreach (string text in source)
{
testThreads.Add(new Thread(() =>
{
Console.WriteLine(text);
}));
}
testThreads.ForEach(t => t.Start())
输出:
other
other
other
答案 0 :(得分:7)
这与闭包捕获变量本身而不进行评估直到它实际使用的事实有关。在foreach循环结束后,text
的值为“other”,并且在循环结束后调用该方法,并在调用时捕获的变量text
的值是“其他”
See this blog post from Eric Lippert了解详情。他解释了行为及其背后的一些原因。
答案 1 :(得分:4)
这是捕获循环变量的经典错误。这会影响for
和foreach
循环:假设一个典型的构造,在循环的整个持续时间内有一个变量。当一个变量被一个lambda表达式或一个匿名方法捕获时,它就是捕获的变量本身(不是捕获时的值)。如果更改变量的值然后执行委托,则委托将“看到”该更改。
Eric Lippert在他的博客中详细介绍了它:part 1,part 2。
通常的解决方案是在循环中获取变量的副本:
string[] source = new string[] {"this", "that", "other"};
List<Thread> testThreads = new List<Thread>();
foreach (string text in source)
{
string copy = text;
testThreads.Add(new Thread(() =>
{
Console.WriteLine(copy);
}));
}
testThreads.ForEach(t => t.Start())
这样做的原因是每个代表现在将捕获copy
变量的不同“实例”。捕获的变量将是为循环迭代创建的变量 - 为该迭代分配text
的值。瞧,这一切都有效。
答案 2 :(得分:2)
C#中的闭包不会在创建时捕获文本的值。由于foreach循环在任何线程执行之前完成执行,因此text
的最后一个值被赋予每个。{/ p>
这可以解决:
string[] source = new string[] {"this", "that", "other"};
List<Thread> testThreads = new List<Thread>();
foreach (string text in source)
{
// Capture the text before using it in a closure
string capturedText = text;
testThreads.Add(new Thread(() =>
{
Console.WriteLine(capturedText);
}));
}
testThreads.ForEach(t => t.Start());
如您所见,此代码在for循环的每次迭代中“捕获”text
的值。这可以保证闭包为每次迭代获得唯一的引用,而不是在最后共享相同的引用。
答案 3 :(得分:0)
发生这种情况的原因是,当你开始你的线程时,循环已经完成,文本局部变量的值是“其他”,所以当你启动那些被打印的线程时。这很容易解决:
string[] source = new string[] {"this", "that", "other"};
foreach (string text in source)
{
new Thread(t => Console.WriteLine(t)).Start(text);
}
答案 4 :(得分:0)
其他人已经解释了为什么你遇到这个问题。
幸运的是,修复非常简单:
foreach (string text in source)
{
string textLocal = text; // this is all you need to add
testThreads.Add(new Thread(() =>
{
Console.WriteLine(textLocal); // well, and change this line
}));
}
答案 5 :(得分:0)
Closures / lambdas无法正确绑定到foreach或循环计数器变量。将值复制到另一个局部变量(未声明为foreach或计数器变量),它将按预期工作。