Skip to content

Instantly share code, notes, and snippets.

@pardeike
Last active October 1, 2022 20:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pardeike/4ee722e572f924e81586b38822ad874b to your computer and use it in GitHub Desktop.
Save pardeike/4ee722e572f924e81586b38822ad874b to your computer and use it in GitHub Desktop.
How C# enumerator methods look like at IL code level
// 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