我理解在C#闭包中提出并回答或讨论了很多问题。但请在我的小实验上给我一点时间......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Timers;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
using (var timer = new Timer(500))
{
timer.AutoReset = false;
GetFunc2(timer, 0);
// GetFunc3(timer, 0);
timer.Start();
Console.ReadLine();
}
}
static void GetFunc2(Timer timer, int i)
{
for (; i < 5; ++i)
{
timer.Elapsed += (obj, e) =>
{
Console.WriteLine(i);
};
}
}
static void GetFunc3(Timer timer, int i)
{
timer.Elapsed += (obj, e) =>
{
Console.WriteLine(i++);
};
timer.Elapsed += (obj, e) =>
{
Console.WriteLine(i++);
};
timer.Elapsed += (obj, e) =>
{
Console.WriteLine(i++);
};
timer.Elapsed += (obj, e) =>
{
Console.WriteLine(i++);
};
timer.Elapsed += (obj, e) =>
{
Console.WriteLine(i++);
};
}
}
}
通过单独调用GetFunc2
中的GetFunc3
和Main
,我们可以看到输出不同,尽管GetFun3
看起来只是GetFunc2
的简单扩展。谁知道为什么?我认为ildasm可以揭示不同的生成代码,但我确实想知道原因。在VS2012 Pro上测试,.net 4.5。
答案 0 :(得分:3)
这个覆盖的次数是压倒性的......
我不会再讨论讨论,但请看Eric Lippert的答案和其他人的答案:
https://stackoverflow.com/a/8899347/1517578
对这些答案的评论也很有趣。
另外,有关它的Eric博客文章的链接在这里:http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx
基本上,您需要将关闭的变量复制到局部变量中:
static void GetFunc2(Timer timer, int i)
{
for (; i < 5; ++i)
{
int i2 = i; // STORE IT
timer.Elapsed += (obj, e) =>
{
Console.WriteLine(i2); // USE THE NEWLY STORED VERSION
};
}
}
这导致:
0
1
2
3
4
..正如所料。
答案 1 :(得分:0)
AFAIK闭包始终包含引用到“parent”方法的局部变量。所以不同之处在于,在GetFunc2中实际调用这些函数之前,您正在递增值。所以在通话时,已经有5的价值。在GetFunc3中,您在提升事件时递增值,因此它将计数。
答案 2 :(得分:0)
如果GetFunc2
在调用(执行)匿名方法之前增加i
,那么当您调用您看到的方法时,i
已经递增。
如果是GetFunc3
,则在调用每个匿名方法之前不会增加i
,因此它从零开始并随着每次调用而不断增加。