Last active
October 1, 2022 20:17
-
-
Save pardeike/4ee722e572f924e81586b38822ad874b to your computer and use it in GitHub Desktop.
How C# enumerator methods look like at IL code level
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// The following C# code from LINQPad (free windows app) uses a method (Test) that | |
// returns an enumeration. Have a look how the IL code that the compiler generates | |
// looks like: | |
static IEnumerable<int> Test() | |
{ | |
yield return 100; | |
var n = Rand.Range(200, 500); | |
yield return n; | |
yield return 600; | |
} | |
// The real method is rather short and contains almost no code: | |
Test: | |
IL_0000: ldc.i4.s FE | |
IL_0002: newobj UserQuery+<Test>d__1..ctor <- create instance of the hidden class | |
IL_0007: ret | |
// The compiler has generated a hidden class called <Test>d__1 | |
// and it has Dispose(), MoveNext(), Reset(), GetEnumerator() as | |
// methods and Current as a getter property. | |
<Test>d__1.System.IDisposable.Dispose: | |
IL_0000: ret | |
All code from the original Test() method is now contained in | |
MoveNext() as one big switch statement. The current element | |
is calculated and stored in UserQuery+<Test>d__1.<>2__current | |
and returned by the Current property. The state machine has | |
a numerical state that starts at case 0 and it is stored in | |
UserQuery+<Test>d__1.<>1__state: | |
<Test>d__1.MoveNext: | |
IL_0000: ldarg.0 | |
IL_0001: ldfld UserQuery+<Test>d__1.<>1__state | |
IL_0006: stloc.0 | |
IL_0007: ldloc.0 | |
IL_0008: switch (IL_001F, IL_0037, IL_005E, IL_0079) <---- switch statement | |
IL_001D: ldc.i4.0 | |
IL_001E: ret | |
IL_001F: ldarg.0 <---- first case/yield | |
IL_0020: ldc.i4.m1 | |
IL_0021: stfld UserQuery+<Test>d__1.<>1__state | |
IL_0026: ldarg.0 | |
IL_0027: ldc.i4.s 100 | |
IL_0029: stfld UserQuery+<Test>d__1.<>2__current | |
IL_002E: ldarg.0 | |
IL_002F: ldc.i4.1 | |
IL_0030: stfld UserQuery+<Test>d__1.<>1__state | |
IL_0035: ldc.i4.1 | |
IL_0036: ret | |
IL_0037: ldarg.0 <---- second case/yield | |
IL_0038: ldc.i4.m1 | |
IL_0039: stfld UserQuery+<Test>d__1.<>1__state | |
IL_003E: ldc.i4 200 | |
IL_0043: ldc.i4 500 | |
IL_0048: call Verse.Rand.Range | |
IL_004D: stloc.1 // n | |
IL_004E: ldarg.0 | |
IL_004F: ldloc.1 // n | |
IL_0050: stfld UserQuery+<Test>d__1.<>2__current | |
IL_0055: ldarg.0 | |
IL_0056: ldc.i4.2 | |
IL_0057: stfld UserQuery+<Test>d__1.<>1__state | |
IL_005C: ldc.i4.1 | |
IL_005D: ret | |
IL_005E: ldarg.0 <---- third case/yield | |
IL_005F: ldc.i4.m1 | |
IL_0060: stfld UserQuery+<Test>d__1.<>1__state | |
IL_0065: ldarg.0 | |
IL_0066: ldc.i4 600 | |
IL_006B: stfld UserQuery+<Test>d__1.<>2__current | |
IL_0070: ldarg.0 | |
IL_0071: ldc.i4.3 | |
IL_0072: stfld UserQuery+<Test>d__1.<>1__state | |
IL_0077: ldc.i4.1 | |
IL_0078: ret | |
IL_0079: ldarg.0 <---- end | |
IL_007A: ldc.i4.m1 | |
IL_007B: stfld UserQuery+<Test>d__1.<>1__state | |
IL_0080: ldc.i4.0 | |
IL_0081: ret | |
<Test>d__1.System.Collections.Generic.IEnumerator<System.Int32>.get_Current: | |
IL_0000: ldarg.0 | |
IL_0001: ldfld UserQuery+<Test>d__1.<>2__current | |
IL_0006: ret | |
<Test>d__1.System.Collections.IEnumerator.Reset: | |
IL_0000: newobj System.NotSupportedException..ctor | |
IL_0005: throw | |
<Test>d__1.System.Collections.IEnumerator.get_Current: | |
IL_0000: ldarg.0 | |
IL_0001: ldfld UserQuery+<Test>d__1.<>2__current | |
IL_0006: box System.Int32 | |
IL_000B: ret | |
<Test>d__1.System.Collections.Generic.IEnumerable<System.Int32>.GetEnumerator: | |
IL_0000: ldarg.0 | |
IL_0001: ldfld UserQuery+<Test>d__1.<>1__state | |
IL_0006: ldc.i4.s FE | |
IL_0008: bne.un.s IL_0022 | |
IL_000A: ldarg.0 | |
IL_000B: ldfld UserQuery+<Test>d__1.<>l__initialThreadId | |
IL_0010: call System.Environment.get_CurrentManagedThreadId | |
IL_0015: bne.un.s IL_0022 | |
IL_0017: ldarg.0 | |
IL_0018: ldc.i4.0 | |
IL_0019: stfld UserQuery+<Test>d__1.<>1__state | |
IL_001E: ldarg.0 | |
IL_001F: stloc.0 | |
IL_0020: br.s IL_0029 | |
IL_0022: ldc.i4.0 | |
IL_0023: newobj UserQuery+<Test>d__1..ctor | |
IL_0028: stloc.0 | |
IL_0029: ldloc.0 | |
IL_002A: ret | |
<Test>d__1.System.Collections.IEnumerable.GetEnumerator: | |
IL_0000: ldarg.0 | |
IL_0001: call UserQuery+<Test>d__1.System.Collections.Generic.IEnumerable<System.Int32>.GetEnumerator | |
IL_0006: ret | |
<Test>d__1..ctor: | |
IL_0000: ldarg.0 | |
IL_0001: call System.Object..ctor | |
IL_0006: ldarg.0 | |
IL_0007: ldarg.1 | |
IL_0008: stfld UserQuery+<Test>d__1.<>1__state | |
IL_000D: ldarg.0 | |
IL_000E: call System.Environment.get_CurrentManagedThreadId | |
IL_0013: stfld UserQuery+<Test>d__1.<>l__initialThreadId | |
IL_0018: ret |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment