C# 为什么c不';是否保留匿名委托调用的上下文?

C# 为什么c不';是否保留匿名委托调用的上下文?,c#,.net,multithreading,c#-4.0,delegates,C#,.net,Multithreading,C# 4.0,Delegates,我有以下方法: static Random rr = new Random(); static void DoAction(Action a) { ThreadPool.QueueUserWorkItem(par => { Thread.Sleep(rr.Next(200)); a.Invoke(); }); } 现在我将其称为for循环,如下所示: for (int i = 0; i < 10; i++) { var

我有以下方法:

static Random rr = new Random();
static void DoAction(Action a)
{
    ThreadPool.QueueUserWorkItem(par =>
    {
        Thread.Sleep(rr.Next(200));
        a.Invoke();
    });
}
现在我将其称为for循环,如下所示:

for (int i = 0; i < 10; i++)
{
    var x = i;

    DoAction(() =>
    {
        Console.WriteLine(i); // scenario 1
        //Console.WriteLine(x); // scenario 2
    });
}
for(int i=0;i<10;i++)
{
var x=i;
DoAction(()=>
{
Console.WriteLine(i);//场景1
//Console.WriteLine(x);//场景2
});
}
在场景1中,输出为:
10。。。10

在场景2中,输出是:
26584。。。0
(0到9的随机排列)


你怎么解释?c不应该为匿名委托调用保留变量(此处为
i
)吗?

这里的问题是有一个
i
变量和十个
x
的实例/副本。每个lambda获取对单个变量
i
x
的一个实例的引用。每个
x
只写入一次,因此每个lambda都会看到写入其引用值的一个值

变量
i
被写入,直到达到10。在循环完成之前,所有lambda都不会运行,因此它们都会看到
i
的最终值,即10

我发现,如果你按照下面的方式重写,这个例子会更清楚一些

int i = 0;  // Single i for every iteration of the loop
while (i < 10) { 
  int x = i;  // New x for every iteration of the loop 
  DoAction(() => {
    Console.WriteLine(i);
    Console.WriteLine(x);
  });
  i++;
};
int i=0;//循环的每个迭代都有一个i
而(i<10){
int x=i;//循环的每次迭代都会有新的x
DoAction(()=>{
控制台写入线(i);
控制台写入线(x);
});
i++;
};

我认为使用java或任何面向对象的语言都会得到相同的结果(不确定,但在这里似乎是合乎逻辑的)

i
的范围适用于整个循环,
x
的范围适用于每次出现


Resharper可以帮助您发现此类问题。

DoAction
生成线程,并立即返回。当线程从随机睡眠中醒来时,循环将完成,
i
的值将一直提升到10。另一方面,
x
的值在调用之前被捕获并冻结,因此您将以随机顺序获得从
0
9
的所有值,这取决于基于随机数生成器的每个线程的睡眠时间。

但是保留('capturing')变量
i
正是发生的事情!虽然您希望这样做,但没有发生的是在设置代理时保留
i
的值。顺便说一句,当显示此代码时,ReSharper将警告您存在错误;您可能会发现这里的解释很有帮助。更重要的是,每个
x
都不是只写入一次,而是一个不同的
x
,因为它每次都被初始化。循环的生命周期中有10个
x
,只有一个
i