C# 奇lambda行为

C# 奇lambda行为,c#,lambda,closures,C#,Lambda,Closures,我偶然发现并发现它非常有趣,所以我自己做了一些测试: 测试一: List<Action> actions = new List<Action>(); for (int i = 0; i < 5; ++i) actions.Add(() => Console.WriteLine(i)); foreach (Action action in actions) action(); List<Action> actions = new

我偶然发现并发现它非常有趣,所以我自己做了一些测试:

测试一:

List<Action> actions = new List<Action>();

for (int i = 0; i < 5; ++i)
    actions.Add(() => Console.WriteLine(i));

foreach (Action action in actions)
    action();
List<Action> actions = new List<Action>();

for (int i = 0; i < 5; ++i)
{
    int j = i;
    actions.Add(() => Console.WriteLine(j));
}

foreach (Action action in actions)
    action();

测试二:

List<Action> actions = new List<Action>();

for (int i = 0; i < 5; ++i)
    actions.Add(() => Console.WriteLine(i));

foreach (Action action in actions)
    action();
List<Action> actions = new List<Action>();

for (int i = 0; i < 5; ++i)
{
    int j = i;
    actions.Add(() => Console.WriteLine(j));
}

foreach (Action action in actions)
    action();


根据这篇文章,在测试一中,所有lambda都包含一个对
i
的引用,这导致它们都输出5。这是否意味着我在测试2中得到了预期的结果,因为为每个lambda表达式创建了一个新的
int

简而言之,for循环的每个循环都引用相同的变量i,因此编译器对所有循环使用相同的lambda表达式

如果有什么安慰的话,这个奇怪之处在javascript中更糟糕,因为javascript只有变量的函数作用域,所以即使您的第二个解决方案也不会做您期望的事情


@Eric Lippert在他的两部分文章中详细解释了这一点:

这是一篇必读的文章,因为它深入地解释了行为和实现级别。

是的

在测试一中,var
i
在循环中被捕获,但
i
指的是一个变量,该变量在循环外部有效声明为,因此所有捕获的lambda都指一个变量。在调用操作时,
i
的值是
5
,因此所有输出都是5


在测试2中,var
j
在循环中被捕获,但在这种情况下,每次
j
在循环中被声明为,因此所有捕获的lambda都引用不同的变量。因此,调用lambda将输出不同的值。

Yes…因为您在每次迭代中将
i
分配给
j
,lambda将捕获对
j
的新引用,并且第二次测试的结果与预期一致。我手头没有Jon Skeet的C#in Depth,所以我无法参考他关于在lambdas中捕获变量的信息。我似乎记得他确实详细介绍了在lambdas中捕获值的过程。如果您有C/C++的背景,可以将其视为传递内存地址,而不是值本身。那么,你通过了&i-,在循环的末尾i==5。j是在作用域内创建的,因此它的值在之后不会被修改。这个问题已经被问了很多次了……这个问题每天都会在JavaScript的上下文中被程序员问几次,他们没有正确地介绍闭包和变量捕获。我不会说这是一个
问题
,而是一个特性。尽管当你第一次遇到它时,它是一个不直观的特性