闭包在for循环和foreach循环中表现不同

时间:2016-02-12 14:43:35

标签: c# for-loop closures

在C#中尝试使用闭包时,我发现如果它们在循环中捕获迭代器变量,它们的工作会出乎意料。

var actions = new List<Action>();

foreach (int i in new[] { 1, 2 })
    actions.Add(() => Console.WriteLine(i));

for (int i = 3; i <= 4; i++)
    actions.Add(() => Console.WriteLine(i));

foreach (var action in actions)
    action();

上面的代码产生了一个奇怪的结果(我使用的是.NET 4.5编译器):

1
2
5
5

为什么i的值在2个几乎相同的循环中被捕获不同?

2 个答案:

答案 0 :(得分:7)

在C#5及更高版本中,i循环为循环的每次迭代声明一个单独的 for变量。因此,每个闭包都会捕获一个单独的变量,并且您会看到预期的结果。

i循环中,您只有一个 foreach变量,该变量由所有闭包捕获,并在循环进行时进行修改 - 所以到时候你调用代理人,你会看到那个单一变量的最终值。

在C#2,3和4中,for循环也表现出这种方式,基本上从不所需的行为,因此在C#5中修复了它。

如果在循环体的范围内引入新变量,则可以在for (int i = 3; i <= 4; i++) { int copy = i; actions.Add(() => Console.WriteLine(copy)); } 循环中实现相同的效果:

var userTicket = new System.Web.HttpContextWrapper(System.Web.HttpContext.Current).GetUmbracoAuthTicket();
if (userTicket != null)
{
    var currentUser = ApplicationContext.Current.Services.UserService.GetByUsername(userTicket.Name);
}

有关详细信息,请阅读Eric Lippert的博文,&#34;关闭循环变量被视为有害的&#34; - part 1part 2

答案 1 :(得分:0)

在foreach的情况下,它将值保存在局部变量中,因此它对每个委托都有自己的值,而在for循环的情况下,它不是那样的,在for循环的情况下,所有的委托都指的是同一个i,所以最后一个所有代表都使用SELECT OBJECT_NAME(OBJECT_ID) AS DatabaseName, last_user_update,* FROM sys.dm_db_index_usage_stats WHERE database_id = DB_ID( 'MyDb') AND OBJECT_ID=OBJECT_ID('dbo.mytable') 中更新的值。

在foreach循环的情况下,这是一个突破性的变化,在旧版本中它们都曾以同样的方式工作。