1

Here is a code sample:

    static int Listing4_31()
    {
        List<int> list = new List<int>() { 1, 2, 3 };
        Print(list);
        return list.Count;
    }

    static void Print(IEnumerable<int> list)
    {
        foreach (var x in list)
        {
            Console.WriteLine(x);
        }
    }

And here is same code but in slightly different form:

private static int Listing4_31()
{
    List<int> obj = new List<int> { 1, 2, 3 };
    Print(obj);
    return obj.Count;
}

private static void Print(IEnumerable<int> list)
{
    using IEnumerator<int> enumerator = list.GetEnumerator();
    while (enumerator.MoveNext())
    {
        Console.WriteLine(enumerator.Current);
    }
}

And here is how this code looks like in ildasm:

.method private hidebysig static void  Print(class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32> list) cil managed
{
  // Code size       49 (0x31)
  .maxstack  1
  .locals init (class [System.Runtime]System.Collections.Generic.IEnumerator`1<int32> V_0,
           int32 V_1)
  IL_0000:  nop
  IL_0001:  nop
  IL_0002:  ldarg.0
  IL_0003:  callvirt   instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<!0> class [System.Runtime]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
  IL_0008:  stloc.0
  .try
  {
    IL_0009:  br.s       IL_001b
    IL_000b:  ldloc.0
    IL_000c:  callvirt   instance !0 class [System.Runtime]System.Collections.Generic.IEnumerator`1<int32>::get_Current()
    IL_0011:  stloc.1
    IL_0012:  nop
    IL_0013:  ldloc.1
    IL_0014:  call       void [System.Console]System.Console::WriteLine(int32)
    IL_0019:  nop
    IL_001a:  nop
    IL_001b:  ldloc.0
    IL_001c:  callvirt   instance bool [System.Runtime]System.Collections.IEnumerator::MoveNext()
    IL_0021:  brtrue.s   IL_000b
    IL_0023:  leave.s    IL_0030
  }  // end .try
  finally
  {
    IL_0025:  ldloc.0
    IL_0026:  brfalse.s  IL_002f
    IL_0028:  ldloc.0
    IL_0029:  callvirt   instance void [System.Runtime]System.IDisposable::Dispose()
    IL_002e:  nop
    IL_002f:  endfinally
  }  // end handler
  IL_0030:  ret
} // end of method Program::Print

My question is: it looks like there is no boxing here?

list.GetEnumerator() returns struct, right? When we convert it to IEnumerator<int> it becomes reference type, correct? And why there is no box instruction?

walruz
  • 1,135
  • 1
  • 13
  • 33
  • 1
    See also https://github.com/dotnet/roslyn/issues/426 and https://stackoverflow.com/questions/42252341/how-to-implement-c-sharp-foreach-optimization-in-il. Essentially the compiler can see the actual type of the enumerator, so it just generates direct `call` instructions. – Charlieface Jun 21 '22 at 20:15

0 Answers0