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

Is there a reason for C##39;s reuse of the variable in a foreach?(C# 在 foreach 中重用变量是否有原因?)
本文介绍了C# 在 foreach 中重用变量是否有原因?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 C# 中使用 lambda 表达式或匿名方法时,我们必须警惕 访问修改闭包 的陷阱.例如:

When using lambda expressions or anonymous methods in C#, we have to be wary of the access to modified closure pitfall. For example:

foreach (var s in strings)
{
   query = query.Where(i => i.Prop == s); // access to modified closure
   ...
}

由于修改了闭包,上面的代码会导致查询中的所有Where子句都基于s的最终值.

Due to the modified closure, the above code will cause all of the Where clauses on the query to be based on the final value of s.

正如这里所解释的,这发生是因为在上面的 foreach 循环中声明的 s 变量在编译器中是这样翻译的:

As explained here, this happens because the s variable declared in foreach loop above is translated like this in the compiler:

string s;
while (enumerator.MoveNext())
{
   s = enumerator.Current;
   ...
}

而不是这样:

while (enumerator.MoveNext())
{
   string s;
   s = enumerator.Current;
   ...
}

正如此处所指出的,在循环外声明变量并没有性能优势,并且在正常情况下我能想到这样做的唯一原因是,如果您打算在循环范围之外使用变量:

As pointed out here, there are no performance advantages to declaring a variable outside the loop, and under normal circumstances the only reason I can think of for doing this is if you plan to use the variable outside the scope of the loop:

string s;
while (enumerator.MoveNext())
{
   s = enumerator.Current;
   ...
}
var finalString = s;

但是,foreach 循环中定义的变量不能在循环外使用:

However variables defined in a foreach loop cannot be used outside the loop:

foreach(string s in strings)
{
}
var finalString = s; // won't work: you're outside the scope.

因此,编译器声明变量的方式使其极易出现通常难以发现和调试的错误,同时不会产生明显的好处.

So 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.

你可以用 foreach 循环做些什么,如果它们是用内部范围的变量编译的,你就不能这样做,或者这只是在匿名方法之前做出的任意选择和 lambda 表达式是可用的或常见的,从那以后没有修改过?

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?

推荐答案

编译器声明变量的方式使其极易出现通常难以发现和调试的错误,同时不会产生明显的好处.

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.

您的批评完全有道理.

我在这里详细讨论这个问题:

I discuss this problem in detail here:

结束循环变量被认为是有害的

是否可以通过这种方式对 foreach 循环执行某些操作,而如果它们是使用内部作用域变量编译的,您就无法做到这一点?或者这只是在匿名方法和 lambda 表达式可用或常见之前做出的任意选择,并且从那时起就没有修改过?

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"循环一致.

The latter. The C# 1.0 specification actually did not say whether the loop variable was inside or outside the loop body, as it made no observable difference. When closure semantics were introduced in C# 2.0, the choice was made to put the loop variable outside the loop, consistent with the "for" loop.

我认为可以公平地说,所有人都对这个决定感到遗憾.这是 C# 中最糟糕的陷阱"之一,我们将采取重大更改来修复它.在 C# 5 中,foreach 循环变量将在逻辑上在内部循环体,因此闭包每次都会得到一个新的副本.

I think it is fair to say that all regret that decision. This is one of the worst "gotchas" in C#, and we are going to take the breaking change to fix it. In C# 5 the foreach loop variable will be logically inside the body of the loop, and therefore closures will get a fresh copy every time.

for 循环不会更改,更改不会向后移植"到以前的 C# 版本.因此,您在使用此成语时应继续小心.

The for loop will not be changed, and the change will not be "back ported" to previous versions of C#. You should therefore continue to be careful when using this idiom.

这篇关于C# 在 foreach 中重用变量是否有原因?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本站部分内容来源互联网,如果有图片或者内容侵犯您的权益请联系我们删除!

相关文档推荐

DispatcherQueue null when trying to update Ui property in ViewModel(尝试更新ViewModel中的Ui属性时DispatcherQueue为空)
Drawing over all windows on multiple monitors(在多个监视器上绘制所有窗口)
Programmatically show the desktop(以编程方式显示桌面)
c# Generic Setlt;Tgt; implementation to access objects by type(按类型访问对象的C#泛型集实现)
InvalidOperationException When using Context Injection in ASP.Net Core(在ASP.NET核心中使用上下文注入时发生InvalidOperationException)
LINQ many-to-many relationship, how to write a correct WHERE clause?(LINQ多对多关系,如何写一个正确的WHERE子句?)