1. C#中的闭包陷阱:foreach循环的坑与解决方案
在C#开发中,闭包是一个强大但容易引发问题的特性。特别是当它与foreach循环结合使用时,会产生一些反直觉的行为。很多开发者都曾在这个问题上栽过跟头,包括我自己。
1.1 什么是闭包?
闭包是指一个函数(或匿名方法)能够访问并记住其词法作用域中的变量,即使该函数在其词法作用域之外执行。在C#中,lambda表达式和匿名方法都会形成闭包。
csharp复制// 简单的闭包示例
Func<int> CreateCounter()
{
int count = 0;
return () => ++count; // 这个lambda捕获了count变量
}
var counter = CreateCounter();
Console.WriteLine(counter()); // 输出1
Console.WriteLine(counter()); // 输出2
1.2 foreach循环中的闭包陷阱
问题通常出现在这样的场景中:
csharp复制var actions = new List<Action>();
var values = new List<int> { 1, 2, 3 };
foreach (var value in values)
{
actions.Add(() => Console.WriteLine(value));
}
foreach (var action in actions)
{
action();
}
你期望的输出是:
code复制1
2
3
但实际输出可能是:
code复制3
3
3
1.3 问题根源分析
这个问题的根源在于C#编译器如何处理foreach循环变量:
-
循环变量的生命周期:在C# 5.0之前,foreach循环中的迭代变量(上例中的value)在整个循环中是同一个变量,每次迭代只是重新赋值。
-
闭包捕获的是变量而非值:lambda表达式捕获的是变量本身,而不是变量在捕获时的值。因此,当循环结束后执行这些action时,它们都会访问同一个value变量,此时它的值是最后一次迭代的值(3)。
-
**编译器生
