Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# ForEach Loop (string declaration in loop)

Tags:

c#

foreach

I have this string array of names.

string[] someArray = { "Messi", "Ronaldo", "Bale", "Neymar", "Pele" };

foreach (string item in someArray)
  {
     Console.WriteLine(item);
  }

When I run in debug mode with breakpoint on foreach line, and using Step Into, string item is highlighted each time with each pass like it declares it again. I suppose it should highlight item only. Does this mean that with each time item is declared again?

Note: using VS 2012.

like image 696
ExpertLoser Avatar asked May 23 '15 01:05

ExpertLoser


2 Answers

The answer here is subtle because the question is (accidentally) unclear. Let me clarify it by breaking it apart into multiple questions.

Is the loop variable logically redeclared every time I go through a foreach loop?

As the other answer correctly notes, in C# 5 and higher, yes. In C# 1 through 4, no.

What is the observable consequence of that change?

In C# 1 there is no way to tell the difference. (And the spec is actually unclear as to where the variable is declared.)

In C# 2 the team added closures in the form of anonymous methods. If there are multiple closures that capture a loop variable in C# 2, 3 and 4 they all capture the same variable, and they all see it mutate as the loop runs. This is almost never what you want.

In C# 5 we took the breaking change to say that a new loop variable is declared every time through the loop, and so each closure captures a different variable and therefore does not observe it changing.

Why does the debugger go back to the loop variable declaration every time I step through the loop?

To inform you that the loop variable is being mutated. If you have

IEnumerable<string> someCollection = whatever;
foreach(string item in someCollection)
{
    Console.WriteLine(item);
}

That is logically equivalent to

IEnumerable<string> someCollection = whatever;
{
    IEnumerator<string> enumerator = someCollection.GetEnumerator();
    try
    {
        while( enumerator.MoveNext() )
        {
            string item;  // In C# 4.0 and before, this is outside the while
            item = enumerator.Current;
            {
                Console.WriteLine(item);
            }
        }
    }
    finally
    {
        if (enumerator != null)
            ((IDisposable)enumerator).Dispose();
    }
}

If you were debugging around that expanded version of the code, you would see the debugger highlight the invocations of MoveNext and Current. Well, the debugger does the same thing in the compact form; when MoveNext is about to be called the debugger highlights the in, for lack of anything better to highlight. When Current is about to be called it highlights item. Again, what would be the better thing to highlight?

So the fact that the debugger highlights the variable has nothing to do with whether the variable is redeclared?

Correct. It highlights the variable when Current is about to be called, because that will mutate the variable. It mutates the variable regardless of whether it is a new variable, in C# 5, or the same variable as before, in C# 4.

I noticed that you changed my example to use a generic sequence rather than an array. Why did you do that?

Because if the C# compiler knows that the collection in the foreach is an array, it actually just generates a good old fashioned for loop rather than calling MoveNext and Current and all that. Doing so is typically faster and produces less collection pressure, so it's a win all around.

However, the C# team wants the debugging experience to feel the same regardless. So again, the code generator creates stepping points on in -- this time where the generated for's loop variable is mutated -- and again, on the code which mutates the foreach loop variable. That way the debugging experience is consistent.

like image 198
Eric Lippert Avatar answered Sep 30 '22 06:09

Eric Lippert


It depends on which version of C# you are targeting. In C# 5, it's a new variable. Before that, the variable was reused.

See Eric Lippert's blog post here: http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx

From the post:

UPDATE: We are taking the breaking change. In C# 5, the loop variable of a foreach will be logically inside the loop, and therefore closures will close over a fresh copy of the variable each time. The "for" loop will not be changed. We return you now to our original article.

like image 37
Chris Shain Avatar answered Sep 30 '22 05:09

Chris Shain