c# - C# 在 foreach 中重用变量是否有原因?

标签 c# foreach lambda scope anonymous-methods

在 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/

相关文章:

c# - 模型的类型与使用语句 MVC View 中指定的类型不同

c# - 使用 Linq 在 JArray 中使用 JSON.NET 多个 orderby

php - 从mysql获取结果并将每个结果应用到变量并最终输出

php - mysql_query行值迭代问题

c# - 使用 OR 运算符的 Lambda 表达式

java - 如何使用 Java Optional 优雅地替换三元运算符

c# - 如果在我的 WPF 项目中找不到 xml 文件,如何编写创建新的 xml?

javascript - 有没有更好的方法来替换我的 double forEach 方法?

foreach 中的 C# 语法不明确

c# - MSMQ 绑定(bind)不匹配