Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
391 views
in Technique[技术] by (71.8m points)

c# - C#在foreach中重用变量是否有原因?(Is there a reason for C#'s reuse of the variable in a foreach?)

When using lambda expressions or anonymous methods in C#, we have to be wary of the access to modified closure pitfall. (在C#中使用lambda表达式或匿名方法时,我们必须警惕对修改后的闭包陷阱的访问 。) For example: (例如:)

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

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 . (由于修改了闭包,上面的代码将使查询上的所有Where子句都基于s的最终值。)

As explained here , this happens because the s variable declared in foreach loop above is translated like this in the compiler: (正如解释在这里 ,这是因为在s中声明的变量foreach上面的循环被翻译这样的编译:)

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

instead of like this: (而不是像这样:)

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;

However variables defined in a foreach loop cannot be used outside the loop: (但是,在foreach循环中定义的变量不能在循环外使用:)

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. (因此,编译器以某种方式声明该变量,使其极易出现通常难以查找和调试的错误,同时不会产生明显的收益。)

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? (是否可以使用foreach循环以这种方式进行处理,如果它们是使用内部作用域变量编译的,则无法做到,还是只是在匿名方法和lambda表达式可用或通用之前做出的任意选择,以及从那以后没有修改过?)

  ask by StriplingWarrior translate from so

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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. (编译器以使变量极易出现通常难以查找和调试的错误的方式声明变量,而不会产生明显的好处。)

Your criticism is entirely justified. (您的批评是完全有道理的。)

I discuss this problem in detail here: (我在这里详细讨论这个问题:)

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? (使用foreach循环,是否可以通过内部作用域变量进行编译而无法做到?) 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? (还是这只是在匿名方法和lambda表达式可用或通用之前做出的任意选择,并且此后没有进行过修改?)

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. (实际上,C#1.0规范没有说明循环变量是在循环体内还是在循环体内,因为它没有明显的区别。) 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#2.0中引入闭包语义时,已做出选择,将循环变量置于循环之外,与“ for”循环一致。)

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. (这是C#中最糟糕的“陷阱”之一, 我们将进行重大更改来修复它。) 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. (在C#5中,foreach循环变量在逻辑上将位于循环体内,因此闭包每次都会获得新的副本。)

The for loop will not be changed, and the change will not be "back ported" to previous versions of C#. (for循环将不会更改,并且更改不会“反向移植”到以前的C#版本。) You should therefore continue to be careful when using this idiom. (因此,在使用此惯用语时,您应继续小心。)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...