40

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?

Community
  • 1
  • 1
a_hardin
  • 4,991
  • 4
  • 32
  • 40
  • 3
    The `foreach` that is being referred to is a C# language feature, not a .NET Framework feature. – BoltClock Aug 24 '12 at 16:06
  • 1
    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 Aug 24 '12 at 17:18
  • 6
    @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 Aug 24 '12 at 17:24
  • @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 Sep 11 '12 at 13:16
  • @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 Sep 11 '12 at 14:00
  • @JeffYates Behavior. I had a small console application for the purpose (not much different from the one in the first comment _to your answer_). Maybe I did something wrong with the recompilation, so it would be nice if someone tried to reproduce/verify. – Jeppe Stig Nielsen Sep 11 '12 at 14:24
  • Changing the langversion to older than C# 5 will still compile using the new C# 5 convention. The langversion checks only the syntax is compatible with the C# version, it does not change the behaviour of the compiled code. See https://github.com/dotnet/roslyn/issues/8034 – Edward Jan 02 '19 at 12:54

1 Answers1

59

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.

Jeff Yates
  • 61,417
  • 20
  • 137
  • 189
  • 6
    An example would be good: `List list = new List(); foreach (var i in new int[] { 1, 2, 3, 4, 5 }) { list.Add(() => Console.WriteLine(i)); } foreach (var f in list) { f(); }` – L.B Aug 24 '12 at 16:16
  • 4
    I'm not sure an example is necessary here as the OP already understands the issue being discussed and is merely looking for confirmation of the change. – Jeff Yates Aug 24 '12 at 16:17
  • 3
    That said, thanks for posting one. I can see value in it for future viewers of this question who need context. Perhaps it can be edited in somehow without confusing things. Thanks. – Jeff Yates Aug 24 '12 at 16:30
  • Would this affect performance as a new variable has to be created for each iteration? – Erwin Mayer Sep 07 '12 at 08:54
  • @ErwinMayer: It might, I don't know without profiling, but I suspect it is negligible and the benefit of not needing to do this when working with closures most likely outweighs any slight performance detriment. – Jeff Yates Sep 07 '12 at 13:27
  • why is this earlier c# spec says similar to C# 5.0 `foreach` http://msdn.microsoft.com/en-GB/library/aa664754.aspx – colinfang Apr 29 '13 at 16:18
  • @colinfang: I do not know. I checked the C#1.2 spec (http://csharpindepth.com/articles/chapter1/Specifications.aspx) and you are correct. It used to be as it is now. However, this was pre-generics and I'm guessing a change was made for C#2.0. – Jeff Yates Apr 30 '13 at 19:13
  • @colinfang: It seems it changed for C#3.0. Interesting that this change was made at that time. I do not know the reason why. – Jeff Yates Apr 30 '13 at 19:17
  • 1
    @colinfang: Based on this comment from Eric Lippert, the spec was wrong prior to C#4 (http://stackoverflow.com/questions/8649337/is-access-to-modified-closure-resolved-by-comprehension-syntax/8649429#comment10746411_8649429) – Jeff Yates Oct 21 '13 at 13:27