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.