在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个几乎相同的循环中被捕获不同?
答案 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 1,part 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循环的情况下,这是一个突破性的变化,在旧版本中它们都曾以同样的方式工作。