What is the Visual Studio C# Compiler Doing in a Foreach Loop

时间:2016-07-11 19:49:55

标签: c# visual-studio foreach

When stepping through C# code, I notice that the debugger stops three times in a foreach declaration:

  • First, it highlights the collection.
  • Then, it highlights the in operator.
  • Last it highlights the variable.

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.

3 个答案:

答案 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:

  1. The debugger first highlights the numbers keyword, here it is calling the GetEnumerator method.
  2. It will then highlight the in keyword, this means calling e.MoveNext()
  3. After that, it will highlight the n variable, this will mean calling e.Current
  4. Finally, it will execute your loop's body, which in our case, is 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.