LoginSignup
1
0

More than 5 years have passed since last update.

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

Posted at

C#のforeachの仕組み(3) IEnumeratorの独自実装コードの続きです。

Niigata.NET 3.0に参加して、forと比較して、foreachをブラックボックスに感じたので、少し整理したい。という話です。
Niigata.NET 3.0に参加してきました

概要

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

コードとIL

forのコード。

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。

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のコード。

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。

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_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 V_1をイテレータとする。
    • Enumerator.MoveNext()の結果を、int32 V_2へ設定する。
    • MoveNext()が偽を返すまで繰り返し。

まとめ

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

1
0
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0