代码检查:在闭包中访问 foreach 变量
首先,让我们确保您理解什么是 闭包。 简单来说,C# 中的闭包是一个 lambda 表达式或匿名方法,它捕获了外部作用域中的一些变量。 以下是最简单的示例:
// A self-contained lambda. Not a closure.
Action printOne = () => { Console.WriteLine("one"); };
// A closure – a lambda that captures a variable from an outer scope.
string myStr = "one";
Action print = () => { Console.WriteLine(myStr); };
在上述示例中, 打印 将捕获变量 myStr (而不是其 值 ),并且仅在您调用 打印() 时获取 myStr 的 值。
在更复杂的场景中,当闭包定义在一个变化的上下文中时,它可能不会按预期运行。
这种情况可能发生在闭包定义在 foreach 语句中,并使用 C# 4.0(Visual Studio 2010)或更早版本编译时。
在 C# 5.0(Visual Studio 2012)之前,编译器会将 foreach 展开为 当...时 语句,并将迭代变量定义在循环外部。 因此,如果此变量在闭包中使用,稍后调用此闭包时总是会获取到循环最后一次迭代对应的值:
var myActions = new List<Action>();
var myStrings = new List<string>() { "one", "two", "three" };
foreach (var str in myStrings)
{
myActions.Add(() => { Console.WriteLine(str); });
}
myActions[0](); // "three" is printed
JetBrains Rider 检测到此问题,并建议通过将循环变量的值复制到定义闭包的作用域中来修复它:
var myActions = new List<Action>();
var myStrings = new List<string>() { "one", "two", "three" };
foreach (var str in myStrings)
{
var str1 = str;
myActions.Add(() => { Console.WriteLine(str1); });
}
myActions[0](); // "one" is printed
此修复确保当您从 myActions 中选择一个操作并获取创建此操作的上下文时, str 将持有相应迭代的值。
最后修改日期: 2025年 9月 26日