1

我试图理解为什么下面的程序会给出它所做的输出。我知道这与参考和价值观有关,但我既不知道术语,也不知道去哪里了解更多信息。

        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

非常感谢解释这类事情的参考资料。

4

3 回答 3

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 必须保留变量,它不能稍后在运行时获取它,因为到那时它就会消失。所以实际上,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.

于 2013-03-13T19:56:50.580 回答
0

这就是闭包在 C# 4.0 及更早版本中的工作方式。变量 x 在 C# 5.0 中更直观(确实是 foreach,而不是所指出的 for 循环)。

闭包每次迭代都会捕获相同的变量。所以你最终得到了最后一个值。

        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
        }
于 2013-03-13T19:47:12.683 回答
0

您将匿名方法/ lambda 传递给ThreadStart对象的构造函数。编译器创建一个包含该匿名方法的类,并将该方法中的局部变量提升x为该y编译器创建的类中的类变量。这就是为什么它能够访问这些变量的值。

请参阅此 MSDN 文章: http: //msdn.microsoft.com/en-us/library/0yw3tz5k (v=vs.80).aspx

于 2013-03-13T19:48:25.990 回答