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在他的两部分文章中详细解释了这一点:
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的上下文中被程序员问几次,他们没有正确地介绍闭包和变量捕获。我不会说这是一个问题,而是一个特性。尽管当你第一次遇到它时,它是一个不直观的特性