When stepping through C# code, I notice that the debugger stops three times in a foreach
declaration:
I'd like to understand what the debugger is doing. For example, it makes sense in a for
loop. During initialization, it assigns the variable, then checks the condition. At the beginning of each subsequent loop, it updates the variable, then checks the condition.
It seems in a foreach
, it would need to get an enumerator at the in operator during initialization, then only assign the next item at the beginning of each loop.
I'm using Visual Studio.
答案 0 :(得分:1)
Consider this example:
IEnumerable<int> numbers = Enumerable.Range(1, 10);
// foreach
foreach (int n in numbers)
{
Console.WriteLine(n);
}
This can be translated to:
IEnumerable<int> numbers = Enumerable.Range(1, 10);
// foreach
IEnumerator<int> e = numbers.GetEnumerator();
while (e.MoveNext())
{
int n = e.Current;
Console.WriteLine(n);
}
So, taking a look at the debugging steps:
numbers
keyword, here it is calling the GetEnumerator
method.in
keyword, this means calling e.MoveNext()
n
variable, this will mean calling e.Current
Console.WriteLine(n)
Steps 2, 3 and 4 are repeated until step 2 returns false, or the enumerator is fully iterated.
答案 1 :(得分:0)
This has nothing to do with the compiler and everything to do with the runtime. That said, you can look at the IL to see what steps are generated. The following is a quick and dirty LinqPad-generated example:
Code:
void Main()
{
var x = new List<string>();
foreach (var y in x) { }
}
IL:
IL_0000: nop
IL_0001: newobj System.Collections.Generic.List<System.String>..ctor
IL_0006: stloc.0 // x
IL_0007: nop
IL_0008: ldloc.0 // x
IL_0009: callvirt System.Collections.Generic.List<System.String>.GetEnumerator
IL_000E: stloc.1
IL_000F: br.s IL_001B
IL_0011: ldloca.s 01
IL_0013: call System.Collections.Generic.List<System.String>+Enumerator.get_Current
IL_0018: stloc.2 // y
IL_0019: nop
IL_001A: nop
IL_001B: ldloca.s 01
IL_001D: call System.Collections.Generic.List<System.String>+Enumerator.MoveNext
IL_0022: brtrue.s IL_0011
IL_0024: leave.s IL_0035
IL_0026: ldloca.s 01
IL_0028: constrained. System.Collections.Generic.List<>.Enumerator
IL_002E: callvirt System.IDisposable.Dispose
IL_0033: nop
IL_0034: endfinally
IL_0035: ret
Instructions IL_0009, IL_0013, and IL_001D are the most interesting. They get the enumerator, find the current item, then move the position to the next position.
答案 2 :(得分:0)
Well collections inherit something called IEnumerable (go to the definition of List
type). IEnumerable has a method called GetNext()
. This gets the next object in the collection. When the next loop is hit, the GetNext() method is executed, allowing the var foo in bar
to be assigned to the object given by GetNext()
. That's why it's highlighted second.