我想试试看,这里发生了什么?编译器生成什么样的代码?
public static void vc()
{
var listActions = new List<Action>();
foreach (int i in Enumerable.Range(1, 10))
{
listActions.Add(() => Console.WriteLine(i));
}
foreach (Action action in listActions)
{
action();
}
}
static void Main(string[] args)
{
vc();
}
Run Code Online (Sandbox Code Playgroud)
输出: 10 10 .. 10
根据这个,ActionHelper的新实例会为每次迭代创建.那么在这种情况下,我认为它应该打印1..10.有人能给我一些编译器在这里做的伪代码吗?
谢谢.
Tig*_*ran 10
在这一行
listActions.Add(() => Console.WriteLine(i));
Run Code Online (Sandbox Code Playgroud)
i捕获变量,或者如果您愿意,创建一个指向该变量的内存位置的指针.这意味着每个委托都有一个指向该内存位置的指针.执行此循环后:
foreach (int i in Enumerable.Range(1, 10))
{
listActions.Add(() => Console.WriteLine(i));
}
Run Code Online (Sandbox Code Playgroud)
由于明显的原因i是10,使得所有的指针存在于存储器内容Action(多个)被指向,变为10.
换句话说,i被捕获.
顺便说一句,应该注意,根据埃里克利珀,这种"奇怪"的行为将被 解决在C# 5.0.
因此在C# 5.0您的程序中将按预期打印:
1,2,3,4,5...10
Run Code Online (Sandbox Code Playgroud)
编辑:
找不到Eric Lippert关于主题的帖子,但这是另一个:
编译器生成什么样的代码?
这听起来像你期望的那样:
foreach (int temp in Enumerable.Range(1, 10))
{
int i = temp;
listActions.Add(() => Console.WriteLine(i));
}
Run Code Online (Sandbox Code Playgroud)
对每次迭代使用不同的变量,因此将捕获创建lambda时变量的值.实际上,您可以使用这个确切的代码并获得您想要的结果.
但是编译器实际上做的更接近于此:
int i;
foreach (i in Enumerable.Range(1, 10))
{
listActions.Add(() => Console.WriteLine(i));
}
Run Code Online (Sandbox Code Playgroud)
这清楚地表明您在每次迭代时捕获相同的变量.当您稍后去实际执行该代码时,它们都会引用已经增加到10 的相同值.
这不是编译器或运行时中的错误......这是语言设计团队的一个有意识的决定.然而,由于对其工作原理的困惑,他们已经改变了这一决定,并决定冒险进行突破性改变,使其更像您对C#5的期望.
| 归档时间: |
|
| 查看次数: |
3197 次 |
| 最近记录: |