在 C# 中使用 lambda 表达式或匿名方法时,我们必须警惕 access to modified closure 陷阱。例如:
foreach (var s in strings)
{
query = query.Where(i => i.Prop == s); // access to modified closure
...
}
由于修改了闭包,上述代码将导致查询中的所有 Where
子句都基于 s
的最终值。
如解释here ,这是因为在上面的 foreach
循环中声明的 s
变量在编译器中被翻译成这样:
string s;
while (enumerator.MoveNext())
{
s = enumerator.Current;
...
}
而不是像这样:
while (enumerator.MoveNext())
{
string s;
s = enumerator.Current;
...
}
正如指出的那样here ,在循环外声明变量没有性能优势,在正常情况下,我能想到这样做的唯一原因是如果您打算在循环范围外使用变量:
string s;
while (enumerator.MoveNext())
{
s = enumerator.Current;
...
}
var finalString = s;
但是,在 foreach
循环中定义的变量不能在循环外使用:
foreach(string s in strings)
{
}
var finalString = s; // won't work: you're outside the scope.
因此,编译器声明变量的方式使其极易出现通常难以发现和调试的错误,同时不会产生明显的好处。
是否可以通过 foreach
循环以这种方式做一些事情,如果它们是使用内部范围变量编译的则不能,或者这只是在匿名方法之前做出的任意选择和 lambda 表达式是可用的或常见的,从那时起哪些还没有被修改?
最佳答案
The compiler declares the variable in a way that makes it highly prone to an error that is often difficult to find and debug, while producing no perceivable benefits.
您的批评是完全有道理的。
我在这里详细讨论这个问题:
Closing over the loop variable considered harmful
Is there something you can do with foreach loops this way that you couldn't if they were compiled with an inner-scoped variable? or is this just an arbitrary choice that was made before anonymous methods and lambda expressions were available or common, and which hasn't been revised since then?
后者。 C# 1.0 规范实际上没有说明循环变量是在循环体内部还是外部,因为它没有明显的区别。在 C# 2.0 中引入闭包语义时,选择将循环变量放在循环之外,与“for”循环一致。
我认为公平地说,所有人都对那个决定感到遗憾。这是 C# 中最糟糕的“陷阱”之一,我们将采取重大更改来修复它。在 C# 5 中,foreach 循环变量在逻辑上内部循环体,因此闭包每次都会得到一个新副本。
for
循环不会更改,更改也不会“向后移植”到以前的 C# 版本。因此,您在使用这个成语时应该继续小心。
关于c# - C# 在 foreach 中重用变量是否有原因?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8898925/