在lambda中使用的外部变量的含义

时间:2013-03-13 19:44:59

标签: c# lambda

我试图理解为什么以下程序提供它所做的输出。我知道它与引用和值有关,但我既不知道术语,也不知道去哪里学习更多。

        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

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

3 个答案:

答案 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对象的构造函数。编译器创建一个包含此匿名方法的类,并将xy从方法中的局部变量提升为此编译器创建的类中的类变量。这就是为什么它能够访问这些变量的值。

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