C#のforeachの仕組み(4) ILにおけるforとの比較

[C#のforeachの仕組み(3) IEnumeratorの独自実装コード](https://qiita.com/y_ohr/items/90870acfa9564e87c123)の続きです。

Niigata.NET 3.0に参加して、forと比較して、foreachをブラックボックスに感じたので、少し整理したい。という話です。
[Niigata.NET 3.0に参加してきました](https://qiita.com/y_ohr/items/e7ef2c22c5da5a675f3f)

# 概要

ILにおいて、forとforeachを比較します。

# コードとIL

forのコード。

``` antlr-csharp:Implement_for
using System;
using System.Collections.Generic;

namespace ConsoleAppFor
{
    class Program
    {
        static void Main()
        {
            var numbers = new List<int>() { 0, 1, 2 };

            for (int i = 0; i < numbers.Count; i++)
            {
                Console.WriteLine(numbers[i]);
            }
        }
    }
}
```
forのIL。

```antlr:IL_for
.method private hidebysig static void  Main() cil managed
{
  .entrypoint
  // コード サイズ       68 (0x44)
  .maxstack  3
  .locals init (class [System.Collections]System.Collections.Generic.List`1<int32> V_0,
           int32 V_1,
           bool V_2)
  IL_0000:  nop
  IL_0001:  newobj     instance void class [System.Collections]System.Collections.Generic.List`1<int32>::.ctor()
  IL_0006:  dup
  IL_0007:  ldc.i4.0
  IL_0008:  callvirt   instance void class [System.Collections]System.Collections.Generic.List`1<int32>::Add(!0)
  IL_000d:  nop
  IL_000e:  dup
  IL_000f:  ldc.i4.1
  IL_0010:  callvirt   instance void class [System.Collections]System.Collections.Generic.List`1<int32>::Add(!0)
  IL_0015:  nop
  IL_0016:  dup
  IL_0017:  ldc.i4.2
  IL_0018:  callvirt   instance void class [System.Collections]System.Collections.Generic.List`1<int32>::Add(!0)
  IL_001d:  nop
  IL_001e:  stloc.0
  IL_001f:  ldc.i4.0
  IL_0020:  stloc.1
  IL_0021:  br.s       IL_0036
  IL_0023:  nop
  IL_0024:  ldloc.0
  IL_0025:  ldloc.1
  IL_0026:  callvirt   instance !0 class [System.Collections]System.Collections.Generic.List`1<int32>::get_Item(int32)
  IL_002b:  call       void [System.Console]System.Console::WriteLine(int32)
  IL_0030:  nop
  IL_0031:  nop
  IL_0032:  ldloc.1
  IL_0033:  ldc.i4.1
  IL_0034:  add
  IL_0035:  stloc.1
  IL_0036:  ldloc.1
  IL_0037:  ldloc.0
  IL_0038:  callvirt   instance int32 class [System.Collections]System.Collections.Generic.List`1<int32>::get_Count()
  IL_003d:  clt
  IL_003f:  stloc.2
  IL_0040:  ldloc.2
  IL_0041:  brtrue.s   IL_0023
  IL_0043:  ret
} // end of method Program::Main
```
foreachのコード。

``` antlr-csharp:Implement_foreach
using System;
using System.Collections.Generic;

namespace ConsoleAppForeach
{
    class Program
    {
        static void Main()
        {
            var numbers = new List<int>() { 0, 1, 2 };

            foreach(var n in numbers)
            {
                Console.WriteLine(n);
            }
        }
    }
}
```
foreachのIL。

```antlr:IL_foreach
.method private hidebysig static void  Main() cil managed
{
  .entrypoint
  // コード サイズ       85 (0x55)
  .maxstack  3
  .locals init (class [System.Collections]System.Collections.Generic.List`1<int32> V_0,
           valuetype [System.Collections]System.Collections.Generic.List`1/Enumerator<int32> V_1,
           int32 V_2)
  IL_0000:  nop
  IL_0001:  newobj     instance void class [System.Collections]System.Collections.Generic.List`1<int32>::.ctor()
  IL_0006:  dup
  IL_0007:  ldc.i4.0
  IL_0008:  callvirt   instance void class [System.Collections]System.Collections.Generic.List`1<int32>::Add(!0)
  IL_000d:  nop
  IL_000e:  dup
  IL_000f:  ldc.i4.1
  IL_0010:  callvirt   instance void class [System.Collections]System.Collections.Generic.List`1<int32>::Add(!0)
  IL_0015:  nop
  IL_0016:  dup
  IL_0017:  ldc.i4.2
  IL_0018:  callvirt   instance void class [System.Collections]System.Collections.Generic.List`1<int32>::Add(!0)
  IL_001d:  nop
  IL_001e:  stloc.0
  IL_001f:  nop
  IL_0020:  ldloc.0
  IL_0021:  callvirt   instance valuetype [System.Collections]System.Collections.Generic.List`1/Enumerator<!0> class [System.Collections]System.Collections.Generic.List`1<int32>::GetEnumerator()
  IL_0026:  stloc.1
  .try
  {
    IL_0027:  br.s       IL_003a
    IL_0029:  ldloca.s   V_1
    IL_002b:  call       instance !0 valuetype [System.Collections]System.Collections.Generic.List`1/Enumerator<int32>::get_Current()
    IL_0030:  stloc.2
    IL_0031:  nop
    IL_0032:  ldloc.2
    IL_0033:  call       void [System.Console]System.Console::WriteLine(int32)
    IL_0038:  nop
    IL_0039:  nop
    IL_003a:  ldloca.s   V_1
    IL_003c:  call       instance bool valuetype [System.Collections]System.Collections.Generic.List`1/Enumerator<int32>::MoveNext()
    IL_0041:  brtrue.s   IL_0029
    IL_0043:  leave.s    IL_0054
  }  // end .try
  finally
  {
    IL_0045:  ldloca.s   V_1
    IL_0047:  constrained. valuetype [System.Collections]System.Collections.Generic.List`1/Enumerator<int32>
    IL_004d:  callvirt   instance void [System.Runtime]System.IDisposable::Dispose()
    IL_0052:  nop
    IL_0053:  endfinally
  }  // end handler
  IL_0054:  ret
} // end of method Program::Main
```

# ILの相違点

```diff:diff_IL
@@ -1,11 +1,11 @@
 .method private hidebysig static void  Main() cil managed
 {
   .entrypoint
-  // コード サイズ       68 (0x44)
+  // コード サイズ       85 (0x55)
   .maxstack  3
   .locals init (class [System.Collections]System.Collections.Generic.List`1<int32> V_0,
-           int32 V_1,
-           bool V_2)
+           valuetype [System.Collections]System.Collections.Generic.List`1/Enumerator<int32> V_1,
+           int32 V_2)
   IL_0000:  nop
   IL_0001:  newobj     instance void class [System.Collections]System.Collections.Generic.List`1<int32>::.ctor()
   IL_0006:  dup
@@ -21,26 +21,33 @@
   IL_0018:  callvirt   instance void class [System.Collections]System.Collections.Generic.List`1<int32>::Add(!0)
   IL_001d:  nop
   IL_001e:  stloc.0
-  IL_001f:  ldc.i4.0
-  IL_0020:  stloc.1
-  IL_0021:  br.s       IL_0036
-  IL_0023:  nop
-  IL_0024:  ldloc.0
-  IL_0025:  ldloc.1
-  IL_0026:  callvirt   instance !0 class [System.Collections]System.Collections.Generic.List`1<int32>::get_Item(int32)
-  IL_002b:  call       void [System.Console]System.Console::WriteLine(int32)
-  IL_0030:  nop
-  IL_0031:  nop
-  IL_0032:  ldloc.1
-  IL_0033:  ldc.i4.1
-  IL_0034:  add
-  IL_0035:  stloc.1
-  IL_0036:  ldloc.1
-  IL_0037:  ldloc.0
-  IL_0038:  callvirt   instance int32 class [System.Collections]System.Collections.Generic.List`1<int32>::get_Count()
-  IL_003d:  clt
-  IL_003f:  stloc.2
-  IL_0040:  ldloc.2
-  IL_0041:  brtrue.s   IL_0023
-  IL_0043:  ret
+  IL_001f:  nop
+  IL_0020:  ldloc.0
+  IL_0021:  callvirt   instance valuetype [System.Collections]System.Collections.Generic.List`1/Enumerator<!0> class [System.Collections]System.Collections.Generic.List`1<int32>::GetEnumerator()
+  IL_0026:  stloc.1
+  .try
+  {
+    IL_0027:  br.s       IL_003a
+    IL_0029:  ldloca.s   V_1
+    IL_002b:  call       instance !0 valuetype [System.Collections]System.Collections.Generic.List`1/Enumerator<int32>::get_Current()
+    IL_0030:  stloc.2
+    IL_0031:  nop
+    IL_0032:  ldloc.2
+    IL_0033:  call       void [System.Console]System.Console::WriteLine(int32)
+    IL_0038:  nop
+    IL_0039:  nop
+    IL_003a:  ldloca.s   V_1
+    IL_003c:  call       instance bool valuetype [System.Collections]System.Collections.Generic.List`1/Enumerator<int32>::MoveNext()
+    IL_0041:  brtrue.s   IL_0029
+    IL_0043:  leave.s    IL_0054
+  }  // end .try
+  finally
+  {
+    IL_0045:  ldloca.s   V_1
+    IL_0047:  constrained. valuetype [System.Collections]System.Collections.Generic.List`1/Enumerator<int32>
+    IL_004d:  callvirt   instance void [System.Runtime]System.IDisposable::Dispose()
+    IL_0052:  nop
+    IL_0053:  endfinally
+  }  // end handler
+  IL_0054:  ret
 } // end of method Program::Main
```

- for
    - int32 V_1をインデックスとしてインクリメントする。
    - V_1 < list.get_Count()の結果を、bool V_2へ設定する(真の場合は1,偽の場合は0)
    - V2が0(=True)になるまで繰り返し。
- foreach
    - Enumerator<int32> V_1をイテレータとする。
    - Enumerator.MoveNext()の結果を、int32 V_2へ設定する。
    - MoveNext()が偽を返すまで繰り返し。

# まとめ

実装した通りでした。
計4回にわたりましたが、現時点でforeachの調査結果は、以上の通りです。
ありがとうございました。