为什么c#不';t为匿名委托调用保留上下文
本文关键字:调用 上下文 保留 为什么 | 更新日期: 2023-09-27 18:20:25
我有以下方法:
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
});
}
在场景1中,输出为:10 10 10 10 ... 10
在场景2中,输出为:2 6 5 8 4 ... 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++;
};
DoAction
生成线程,并立即返回。当线程从随机睡眠中醒来时,循环将结束,i
的值将一直前进到10。另一方面,x
的值在调用之前被捕获并冻结,因此您将以随机顺序获得从0
到9
的所有值,这取决于基于随机数生成器的每个线程的睡眠时间。
我认为使用java或任何面向对象的语言都会得到相同的结果(不确定,但在这里它似乎是合乎逻辑的)。
i
的范围用于整个循环,x
的范围用于每次出现。
Resharper可以帮你找出这类问题的症结所在。