我试图理解为什么以下程序提供它所做的输出。我知道它与引用和值有关,但我既不知道术语,也不知道去哪里学习更多。
for (int x = 0; x < 2; x++)
{
int y = x;
new Thread(new ThreadStart(() =>
{
Thread.Sleep(100);
Console.WriteLine("Thread sees x = {0}, y = {1}", x, y);
})).Start();
}
Thread.Sleep(1000);
输出:
Thread sees x = 2, y = 0
Thread sees x = 2, y = 1
非常感谢解释此类事情的参考资料。
答案 0 :(得分:2)
注意:这实际上不是完整的故事,我可能有些不对劲,但这个想法仍然存在。
这是因为关闭。
闭包发生在for循环中(过去常常在foreach循环中发生,但在C#5中发生了变化)。编译的内容是这样的:
int x = 0;
while (x < 2)
{
int y = x;
new Thread(new ThreadStart(() =>
{
Thread.Sleep(100);
Console.WriteLine("Thread sees x = {0}, y = {1}", x, y);
})).Start();
x++;
}
Thread.Sleep(1000);
由于x
存在于for循环范围之外,因此lambda不会接受。所以它仍然是x
。但是,当y
的范围在for循环之外结束时,lambda HAS保留变量,它不能在以后运行它,当它运行时,它会消失。所以事实上,lambda在运行时是这样的:
首次循环运行:
() => Thread.Sleep(100); Console.WriteLine("Thread sees x = {0}, y = {1}, x, 0);
第二次循环运行:
() => Thread.Sleep(100); Console.WriteLine("Thread sees x = {0}, y = {1}, x, 1);
当lambda实际运行时,x
已经2
,并且因为它没有将其带入,它会将其读作2
。
答案 1 :(得分:0)
这就是closure在C#4.0及更早版本中的工作原理。变量x在C#5.0中更直观(实际上对于foreach,而不是指出的循环)。
Closure每次迭代捕获相同的变量。所以你最终得到了最后一个值。
List<Action> actions = new List<Action>();
for (int i = 0; i < 10; i++)
{
Action anonymousFunction = () => Console.WriteLine(i);
actions.Add(anonymousFunction);
}
foreach (var action in actions)
{
action();//prints out the last value every time
}
但是,您可以通过复制新值来更改行为。
List<Action> actions = new List<Action>();
for (int i = 0; i < 10; i++)
{
int copyOfOriginalValue = i;
Action anonymousFunction = () => Console.WriteLine(copyOfOriginalValue);
actions.Add(anonymousFunction);
}
foreach (var action in actions)
{
action();//prints out unique values
}
答案 2 :(得分:0)
您正在将匿名方法/ lambda传递给ThreadStart
对象的构造函数。编译器创建一个包含此匿名方法的类,并将x
和y
从方法中的局部变量提升为此编译器创建的类中的类变量。这就是为什么它能够访问这些变量的值。
请参阅此MSDN文章:http://msdn.microsoft.com/en-us/library/0yw3tz5k(v=vs.80).aspx