使用lambda表达式时,c#中的闭包是如何工作的?

Oop*_*ser 11 c# lambda

在以下教程中:http://www.albahari.com/threading/

他们说以下代码:

for (int i = 0; i < 10; i++)
  new Thread (() => Console.Write (i)).Start();
Run Code Online (Sandbox Code Playgroud)

是非确定性的,可以产生以下答案:

0223557799

我认为当使用lambda表达式时,编译器会创建某种匿名类,通过在捕获类中创建类似它们的成员来捕获正在使用的变量.但是i值类型,所以我认为他应该被价值复制.

我的错在哪里?

如果答案将解释闭包如何工作,如何保存特定int的"指针",在这种特定情况下生成了什么代码,那将非常有用?

Ser*_*rvy 8

这里的关键点是闭包关闭变量而不是值.因此,在您关闭它时给定变量的值是无关紧要的.重要的是调用匿名方法时该变量的值.

当您看到编译器将闭包转换为什么时,这很容易看到.它会在道德上创造出类似于此的东西:

public class ClosureClass1
{
    public int i;

    public void AnonyousMethod1()
    {
        Console.WriteLine(i);
    }
}

static void Main(string[] args)
{
    ClosureClass1 closure1 = new ClosureClass1();
    for (closure1.i = 0; closure1.i < 10; closure1.i++)
        new Thread(closure1.AnonyousMethod1).Start();
}
Run Code Online (Sandbox Code Playgroud)

所以在这里我们可以更清楚地看到发生了什么.该变量有一个副本,该变量现在已被提升为新类的字段,而不是本地变量.现在修改局部变量的任何地方都会修改此实例的字段.我们现在可以看到为什么你的代码打印它的作用.在启动新线程之后,但在它实际执行之前,for主线程中的循环将返回并在闭包中递增变量.闭包尚未读取的变量.

要产生所需的结果,您需要做的是确保不是让循环的每个迭代都关闭单个变量,而是每个迭代都需要一个它们关闭的变量:

for (int i = 0; i < 10; i++)
{
    int copy = i;
    new Thread(() => Console.WriteLine(copy));
}
Run Code Online (Sandbox Code Playgroud)

现在copy变量在关闭后永远不会改变,我们的程序将打印出0-9(尽管以任意顺序,因为线程可以按照OS的要求进行调度).