Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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;    ... } 

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(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?

like image 574
StriplingWarrior Avatar asked Jan 17 '12 17:01

StriplingWarrior


People also ask

Why do females have C-sections?

A c-section may be needed to protect the health of your or your baby. In these cases, a c-section may be safer than vaginal birth. Your provider may want to schedule your c-section if there are problems during pregnancy that make vaginal birth risky.

Are C-sections common?

About 30% of all babies in the U.S. are born via C-section, so they're fairly common. C-sections are safe for mothers and babies. But it is major surgery, so you shouldn't take it lightly.

Why C-section has become common?

Evolution of the C-section For one, mothers rarely die during C-sections, thanks to advances in care. The advent of anesthesia makes the process less painful. Quality antibiotics also decrease the risk for life-threatening infections. An estimated 32.2 percent of all babies are born through C-section.

Are C-sections worse than natural birth?

Women are three times more likely to die during a cesarean delivery than during a vaginal birth, due mostly to blood clots, infections and complications from anesthesia, according to a 2006 study published in the journal Obstetrics & Gynecology.


1 Answers

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

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.

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.

like image 57
Eric Lippert Avatar answered Dec 10 '22 04:12

Eric Lippert