39
votes

In this answer https://stackoverflow.com/a/8649429/1497 Eric Lippert says that "FYI we are highly likely to fix this in the next version of C#; this is a major pain point for developers" with regards to how the foreach loops uses the variable.

In the next version each time you run through the "foreach" loop we will generate a new loop variable rather than closing over the same variable every time. This is a "breaking" change but in the vast majority of cases the "break" will be fixing rather than causing bugs.

I have not been able to find anything indicating that this change has been made yet. Is there any indication that this is how the foreach loop will work in C# 5?

1
The foreach that is being referred to is a C# language feature, not a .NET Framework feature. - BoltClock
Thanks @BoltClock. I've updated the question to target C# 5. For clarification, though, if I am building a C# project in VS 2012 targeting .Net 4.5, am I likely using C# 5? - a_hardin
@a_hardin: You will be using C#5.0 for any project in VS2012 regardless of target framework unless you ask it to use a different compiler. To change the compiler to specific version, go to Project properties, Build tab and click Advanced... at the bottom-right. Then change "Language Version" dropdown. - Jeff Yates
@JeffYates I tried changing the language version in VS2012, but it keeps following the "new" convention (i.e. the foreach variable is an "inside" variable, and a different "copy" is captured in each iteration). Have you actually verified that changing the language version works, for you? (This is mostly of theoretical interest; I don't think anyone would want the C# 4 behavior in practice). - Jeppe Stig Nielsen
@JeppeStigNielsen: Interesting. I hadn't tried it. Perhaps it will only honour syntax differences and not spec changes. That's surprising. I'll have to take a look when I get chance. HOw did you verify? IL or app behavior? - Jeff Yates

1 Answers

58
votes

This is a change to the C# language, not the .NET framework. Therefore, it only affects code compiled under C# 5.0, regardless of the .NET framework version on which that code will execute.

C# 5.0

Section 8.8.4 of the specification makes it clear that this change has been made. Specifically, page 249 of the C# 5.0 specification states:

foreach (V v in x) embedded-statement

is then expanded to:

{
    E e = ((C)(x)).GetEnumerator();
    try {
        while (e.MoveNext()) {
            V v = (V)(T)e.Current;
            embedded-statement
        }
    }
    finally {
        … // Dispose e
    }
}

And later:

The placement of v inside the while loop is important for how it is captured by any anonymous function occurring in the embedded-statement.

C# 4.0

This change to the specification is clear when comparing with the C# 4.0 specification which states (again, in section 8.8.4, but this time, page 247):

foreach (V v in x) embedded-statement

is then expanded to:

{
    E e = ((C)(x)).GetEnumerator();
    try {
        V v;
        while (e.MoveNext()) {
            v = (V)(T)e.Current;
            embedded-statement
        }
    }
    finally {
        … // Dispose e
    }
}

Note that the variable v is declared outside the loop instead of inside, as it is with C# 5.0.

Note

You can find the C# specification in the installation folder of Visual Studio under VC#\Specifications\1033. This is the case for VS2005, VS2008, VS2010 and VS2012, giving you access to specifications for C# 1.2, 2.0, 3.0, 4.0 and 5.0. You can also find the specifications on MSDN by searching for C# Specification.