Foreach and Iterator - Expert C# 5.0: with .NET 4.5 Framework (2013)

Expert C# 5.0: with .NET 4.5 Framework (2013)

CHAPTER 9

images

Foreach and Iterator

This chapter will discuss the foreach statement and iterators block. The foreach statement is used to iterate an array or an object collection that implements the System.Collections.IEnumerable or System.Collections.Generic.IEnumerable<T> interface. The iterators block is also used to iterate through an array or an object collection based on the state machine generated automatically by the C# compiler.

State Machine

Throughout this chapter, you will explore how the C# compiler generates the state machine for an iterator block, what the states are that the state machine contains, and how the state transition takes place in the iterator sections. Therefore, it’s important to begin this chapter with a brief overview of state machines.

The C# compiler automatically generates a state machine for the iterator code block when an iterator method is defined in a program. A state machine is a model by which an object alters its behavior when its internal state changes in response to events. A state is a unique condition in which a state machine does some specific action in its lifetime. An event in context of the state machine is something that triggers the state machine to do a transition, and a transition demonstrates the actions when a state machine receives an event depending on its current state. An actionrefers to what a state machine performs during a transition.

In .NET, the C# compiler generates the state machine for the iterator block used in a program and maintains its state on each of the transitions of the state based on an event (called the MoveNext method, which we will explore later in this chapter). For each of the transitions in the state machine, the CLR does some action, for example, process the iterated item that was defined in the respective state and return it to the caller.

Foreach Statement

The foreach statement iterates over each of the elements in a collection and associated statements will process each of the elements it retrieves from the collection on iteration. The following section explains how to declare a foreach statement in a program, and later you will explore how the C# compiler compiles the foreach statement used in a program and also how the CLR handles it in runtime.

Declaration

The foreach statement declared in the program will have the following syntax:

foreach ( local_variable_type(LT) iteration_variable(IV) in an_expression(E) )
{
Statement or Statements (S)
}

The local variable type (LT) defines the type of the iteration variable (IV) but if var is used for the LT, it is said to be an implicitly typed iteration variable and its type is the element type of the Enumerator object that we get from the expression E. The iteration variable can’t be updated from theStatement or Statements (S) used for the foreach block or a compile-time error occurs, as shown below, while trying to update the iteration variable number from the statement block of the foreach statement, as shown in Listing 9-1.

Error 19 Cannot assign to 'number' because it is a 'foreach iteration variable'

Listing 9-1. An Example of a Foreach Statement

using System;
using System.Collections.Generic;

namespace Ch09
{
class Program
{
static void Main(string[] args)
{
IList<int> numbers = new List<int>
{
1,2,3,4,4,6,7,8,9,10
};

foreach (int number in numbers)
{
Console.Write("{0}\t", number);
}
}
}
}

The program in Listing 9-1 produces the following output:

1 2 3 4 4 6 7 8 9 10

Internal of the Foreach

In the following sections, we will explore how the C# compiler treats a foreach statement in compile time and also how the CLR takes care of it in runtime when there is a foreach statement used in a program.

In Compile Time

The C# compiler determines the collection type of the expression E (in foreach declaration), enumerator type (get the enumerator type from the E), and element type (type for the iteration variable for the foreach declaration) for the foreach statement before it starts iterating the collection. To determine the collection type, enumerator type, and element type, the C# compiler completes various checks, as outlined in the following sections.

Array Type

If the type T of expression (E) is an array type, then there is an implicit reference conversion from T to the IEnumerable interface (since System.Array implements this interface). The collection type is the IEnumerable interface, the enumerator type is the IEnumerator interface, and the element type is the element type of array type T. The IEnumerable and IEnumerator are defined in the System.Collections namespace.

Dynamic Type

If the type T of expression (E) used in the foreach statement is dynamic, then there is an implicit conversion from expression to the IEnumerable interface.

Other

If the collection type can’t be determined in the array and dynamic type check stage, then the C# compiler determines whether the type T has an appropriate GetEnumerator method, and if the GetEnumerator is found, then it does the following:

· Checks whether the return type E of the GetEnumerator method is a class, struct, or interface type or otherwise terminates the operation.

· Performs a member lookup on E for the property Current and method MoveNext. If the lookup returns true, then the operation will continue or otherwise terminates the operation.

After determining the collection type T, the enumerator type E, and the element type, the CLR starts iterating the collection.

In Runtime

From Listing 9-1, you can see that the C# compiler found the collection type of the numbers, which is System.Collections.Generic.IEnumerable'1<int32>, and retrieved the Enumerator object System.Collections.Generic.IEnumerator'1<int32> from the numbers. It iterates through the Enumerator object to get the value from thenumbers and assigns it to the iteration variable number. Figure 9-1 demonstrates how the foreach statement works in runtime based on the example in Listing 9-1.

images

Figure 9-1. Foreach in runtime

Figure 9-1 shows that:

· The CLR gets the Enumerator object from the numbers collection by calling the GetEnumerator method of the collection type determined against the numbers (used in Listing 9-1).

· The CLR calls the MoveNext method of the Enumerator, and if the return of MoveNext method is true, it calls the get_Current method to get the current item from the collection and processes the element if there is any.

The CLR keeps calling the MoveNext method of the Enumerator object until the MoveNext method return false.

Internal of the Foreach Execution

Let’s decompile Listing 9-1 using the ildasm.exe tool to understand how the CLR handles the foreach statement, as shown in Listing 9-2.

Listing 9-2. IL Code for the Foreach Statement

.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 161 (0xa1)
.maxstack 2
.locals init (
[0] class [mscorlib]System.Collections.Generic.IList'1<int32>
numbers,
[1] int32 number,
[2] class [mscorlib]System.Collections.Generic.List'1<int32>
'<>g__initLocal0',
[3] class [mscorlib]System.Collections.Generic.IEnumerator'1<int32>
CS$5$0000,
[4] bool CS$4$0001)
IL_0000: nop
IL_0001: newobj instance void class
[mscorlib]System.Collections.Generic.List'1<int32>::.ctor()
IL_0006: stloc.2
IL_0007: ldloc.2
IL_0008: ldc.i4.1
IL_0009: callvirt instance void class
[mscorlib]System.Collections.Generic.List'1<int32>::Add(!0)
/* Rest of the Add method call removed */

/* Get Enumerator*/
/* CLR loads the Enumerator object from the collection instantiated
* in IL_0001 on to the evaluation stack*/

IL_005d: callvirt instance class
[mscorlib]System.Collections.Generic.IEnumerator'1<!0> class
[mscorlib]System.Collections.Generic.IEnumerable'1<int32>::
GetEnumerator()

/* Store the Enumerator object returned from the IL_005d into the Local
* variable CS$5$0000 at position 3. */
IL_0062: stloc.3

/* Compiler wrap the foreach iteration block into the try block when use a
* foreach statement*/
.try
{
/* Transfer the program control in IL_007f to load the Enumerator
* object CS$5$0000 from the local variable at position 3 and continue
* the operation from there.*/
IL_0063: br.s IL_007f

/* CLR loads the Enumerator object from the collection type instantiated
* in IL_0001 on to the evaluation stack*/
IL_0065: ldloc.3

/* get_Current method of the Enumerator returns the Current element. */
IL_0066: callvirt instance !0 class
[mscorlib]System.Collections.Generic.IEnumerator'1<int32>::
get_Current()

/* Store the value return from IL_0066 in the Local variables section
* at position 1 and do related operation*/
IL_006b: stloc.1
IL_006c: nop
IL_006d: ldstr "{0}\t"
IL_0072: ldloc.1
IL_0073: box [mscorlib]System.Int32
IL_0078: call void [mscorlib]System.Console::Write(string, object)
IL_007d: nop
IL_007e: nop

/* CLR loads the Enumerator object from the collection type instantiated
* in IL_0001 on to the evaluation stack*/
IL_007f: ldloc.3

/* Execute the MoveNext method to find out whether it is
* possible to iterate the collection any further. The MoveNext
* method returns a boolean value , on true it will iterate
* through the list or otherwise on false it will
* execute the finally block to terminate the iteration.*/
IL_0080: callvirt instance bool
[mscorlib]System.Collections.IEnumerator::MoveNext()
IL_0085: stloc.s CS$4$0001
IL_0087: ldloc.s CS$4$0001

/* Transfer the program control to the IL_0065 to continue the iteration.
* If the MoveNext return true from IL_0080.*/
IL_0089: brtrue.s IL_0065
IL_008b: leave.s IL_009f
} // end .try

finally /* Finally block will execute regardless to dispose the iterator */
{
IL_008d: ldloc.3
IL_008e: ldnull
IL_008f: ceq
IL_0091: stloc.s CS$4$0001
IL_0093: ldloc.s CS$4$0001
IL_0095: brtrue.s IL_009e
IL_0097: ldloc.3
IL_0098: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_009d: nop
IL_009e: endfinally
} // end handler

IL_009f: nop
IL_00a0: ret
}

Images Note: br.s target: Branch to target.
brtrue.s target: Branch to target if value is nonzero (true).

Let’s analyze Listing 9-2 to understand the underlying foreach execution.

Get Enumerator

In IL_005d, the CLR retrieves the Enumerator object by calling the GetEnumerator method of the collection type List'1<int32> instantiated in IL_0001. On a successful return of the GetEnumerator method, the CLR stores the iterator object in CS$5$0000 in the Local variable section of the Main Method.

Execute MoveNext

When the program control moves in IL_0063, it transfers the program control to the IL_007f to call the MoveNext method of the Enumerator object returned from the GetEnumerator method in IL_005d. The CLR loads the Enumerator object CS$5$0000 stored at the local variable section at position 3 and calls theMoveNext method of that Enumerator object to make sure the iteration is possible over the collection. On return of true from the MoveNext method, the CLR processes the associated embedded statement with the foreach statement using the current iterated item.

To get the current item, the CLR transfers the program control in IL_0065 and loads the Enumerator object stored in the Local section at position 3. It gets the current item from the Enumerator object by calling the get_Current method of the Enumerator object in IL_0066. In IL_006b to IL_007e, the CLR processes the iterated item, for example, by display on the console as output.

End of Iteration

When the CLR calls the MoveNext method in IL_0080 and on return of true, the CLR continues, as discussed in the “Execute MoveNext” section. On return of false, the CLR executes the leave instruction in IL_008b, which will execute the nearest finally block defined in IL_008d to IL_009e. After finishing the execute instruction in IL_008d to IL_009e, the CLR transfers the program control to the IL_009f and from IL_00a0 it will return from this method.

The false return from the MoveNext method denotes that there are no more items to iterate through in the collection object and this is the end of the operation.

So far we have explored how the foreach statement works in C#; now we will see how to use the foreach statement in the iterator block and explore in detail how the iterator is used in C#.

Iterators

When a function member is implemented using an iterator block (a block that contains one or more yield statements), it is referred to as an iterator. When the C# compiler finds any yield statement in a method, it generates a class, which is a state machine used to implement the behavior that is expressed in the iterator block. On each yield statement, control is returned to the caller, but the CLR maintains the state of the callee into the state machine the compiler generated for it. The iterator block can be declared as shown in Figure 9-2.

images

Figure 9-2. Iterators declaration

The return type of the iterator block is one of the following:

· Enumerator: System.Collections.IEnumerator or System.Collections.Generic.IEnumerator<T>.

· Enumerable: System.Collections.IEnumerable or System.Collections.Generic.IEnumerable<T>.

Let’s look at the example provided in Listing 9-3 where an iterator block is defined for the Power method using the yield keyword. The Power method calculates the power of a number based on a given exponent and returns the current iterated item, which results in the power of the number to the caller. It will continue iteration until it reaches the termination condition (counter++< exponent).

Listing 9-3. An Example of the Iterator

using System;
using System.Collections;

namespace Ch09
{
class Program
{
static void Main(string[] args)
{
int currentExponent = 0;
int iterationPhase = 0;
Console.WriteLine("{0,10}\t{1,10}\t{2,10}",
"Iteration Phase", "Power", "Next Power will be");
foreach (int i in Power(2, 4))
{
currentExponent = i;
Console.Write("{0,9}\t{1,9}",
++iterationPhase, currentExponent);
}
}

public static IEnumerable Power(int number, int exponent)
{
int counter = 0;
int result = 1;
while (counter++ < exponent)
{
result = result * number;
yield return result;
Console.WriteLine("\t{0,9}x{1}\t\u25BC", result, number);
}
}
}
}

This program will produce the following output:

Iteration Phase Power Next Power will be
1 2 2x2 ▼
2 4 4x2 ▼
3 8 8x2 ▼
4 16 16x2 ▼

Listing 9-3 shows that the CLR calls the Power method from the foreach statement and calculates the power of a number. The Power method will be called as long as the condition specified in the while statement (counter++< exponent) is valid. On each iteration, behind the scenes the CLR maintains the state of the Power method in a C# compiler–generated state machine <Power>d__0. The state stored in the state machine is used to iterate through the collection. On each iteration, the CLR loads the state from the state machine and changes the state to the running state to continue the operation. It checks the condition (the condition specified in while statement) whether or not the next iteration is valid to continue, stores back the state and related information in the state machine, and returns to the caller. The CLR will continue this operation as long as the call of the MoveNext method of the state machine returns the true result.

The <Power>d__0 state machine is extracted from the executable, which is produced from Listing 9-3 using the ildasm.exe tool, as shown in Figure 9-3.

images

Figure 9-3. <Power>d__0 state machine explored via the ildasm.exe tool

This state machine implements the MoveNext method, which is used to control the state transition of the state machine. On a successful true return of the MoveNext method, iteration will continue; but upon a false return, the iteration will terminate.

Iterator Internal

In Figure 9-3, we saw that the C# compiler generated the <Power>d__0 state machine for Listing 9-3. In the following section, we will explore in depth the <Power>d__0 state machine.

Iterator and State Machine

The <Power>d__0 state machine has four states that maintain the state of the state machine used for the iterator block. Table 9-1 lists the states used in the <Power>d__0 state machine for the iterator block.

Images

State Transition

In the state transition of the <Power>d__0 state machine, the CLR executes specific tasks defined for the state, as shown in Table 9-2.

Images

The MoveNext method of <Power>d__0 state machine is responsible for the state transition in the state machine to process the collection. Let’s explore the MoveNext method, as presented in Figure 9-4, which shows the state transition of the <Power>d__0 state machine. The MoveNext method of the <Power>d__0state machine is extracted from the executable produced from the code in Listing 9-3 using the .Net Reflector tool.

images

Figure 9-4. State transition of the state machine <Power>d__0

Explore the State Machine <Power>d__0

Let’s decompile the code in Listing 9-3 using the ildasm.exe tool to understand the underlying works of the iterator. The decompiled IL code is divided into three categories:

· Foreach: In this decompiled IL code block (Listing 9-4), you can see how the CLR iterates through the Enumerator object returned from the iterator block.

· Instantiation of the state machine (<Power>d__0): This method (Listing 9-5) instantiates an instance of the state machine <Power>d__0.

· State machine (<Power>d__0): The decompiled IL code (Listing 9-6) of the <Power>d__0 state machine shows the internal implementation of the state machine.

Process Iterator Block Using the Foreach Statement

Let’s decompiled the IL code for the Main method, as shown in Listing 9-4.

Listing 9-4. Decompiled Main Method

.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 4
.locals init (
[0] int32 currentExponent,
[1] int32 iterationPhase,
[2] int32 i,
[3] class [mscorlib]System.Collections.IEnumerator CS$5$0000,
[4] bool CS$4$0001,
[5] class [mscorlib]System.IDisposable CS$0$0002)
L_0000: nop

/* Code removed */
L_0020: ldc.i4.2
L_0021: ldc.i4.4

/* Initialize the state machine */
/* Power method will return an instance of the of <Power>d__0 which
* encapsulates the iterator */
L_0022: call class [mscorlib]System.Collections.IEnumerable
Ch09.Program::Power(int32, int32)

/* Get the Enumerator from the collection/enumerable return in L_0022 */
L_0027: callvirt instance class [mscorlib]System.Collections.IEnumerator
[mscorlib]System.Collections.IEnumerable::GetEnumerator()
L_002c: stloc.3

/* Transfer the program control to the L_005a to execute the MoveNext
* method of the Enumerator to find out whether the iteration should
* continue or not. */
L_002d: br.s L_005a
L_002f: ldloc.3

/*Get the Current item from the Enumerator */
L_0030: callvirt instance object
[mscorlib]System.Collections.IEnumerator::get_Current()
L_0035: unbox.any int32
L_003a: stloc.2
L_003b: nop
L_003c: ldloc.2
L_003d: stloc.0

/* Process the return item from the Enumerator in L_0030
* if there any exists */
L_003e: ldstr "{0,9}\t{1,9}"
L_0043: ldloc.1
L_0044: ldc.i4.1
L_0045: add
L_0046: dup
L_0047: stloc.1
L_0048: box int32
L_004d: ldloc.0
L_004e: box int32
L_0053: call void [mscorlib]System.Console::Write(string, object, object)
L_0058: nop
L_0059: nop

/* Loads the Enumerator object (CS$5$0000) stored at position 3 */
L_005a: ldloc.3

/* Check whether there is any item in the list or not by
* calling the MoveNext method which will make sure whether
*there is any item in the list by return a boolean value
* true or false*/
L_005b: callvirt instance bool
[mscorlib]System.Collections.IEnumerator::MoveNext()
L_0060: stloc.s CS$4$0001
L_0062: ldloc.s CS$4$0001

/* On true return the CLR will transfer the program control
* to the L_002f to keep continue the processing */
L_0064: brtrue.s L_002f

/* On false CLR will execute nearest finally block which indicate the
* end of iteration. */
L_0066: leave.s L_0084
L_0068: ldloc.3
/* Code removed */
L_0084: nop
L_0085: ret
/* try finally embedded by the C# compiler for the foreach statement. */
.try L_002d to L_0068 finally handler L_0068 to L_0084
}

In Listing 9-4, the CLR calls the MoveNext method of the Enumerator to iterate through the collection. The Enumerator object extracted from the IEnumerable object is returned in L_0022, as you saw earlier, because the CLR finds the collection type for the expression used in the foreach statement. In this circumstance, it will be the C# compiler–generated enumerator or state machine.

In Listing 9-3, the expression Power(2,4) is used in the foreach statement, which instantiates an instance of the C# compiler–generated Enumerator object or the state machine for the iterator block. In IL_0027 and IL_002c, the CLR loads and stores the instance of the Enumerator object returned from thePower method. The CLR calls the MoveNext method to iterate through the Enumerator object to continue to call the MoveNext method until the MoveNext method returns false.

Instantiation of the State Machine (<Power>d__0)

The C# compiler generates the stub method shown in Listing 9-5, which is used to instantiate the state machine <Power>d__0. Let’s decompiled the stub method in IL for the Power method, which was used to instantiate an instance of the state machine <Power>d__0.

Listing 9-5. Decompiled Power Method

.method public hidebysig static class [mscorlib]System.Collections.IEnumerable
Power(int32 number, int32 exponent) cil managed
{
.maxstack 2
.locals init (
[0] class Ch09.Program/<Power>d__0 d__,
[1] class [mscorlib]System.Collections.IEnumerable enumerable)
L_0000: ldc.i4.s -2

/* Instantiate the state machine generated by the C# compiler */
L_0002: newobj instance void Ch09.Program/<Power>d__0::.ctor(int32)
/* Code removed */

L_000a: stfld int32 Ch09.Program/<Power>d__0::<>3__number
/* Code removed */

L_0011: stfld int32 Ch09.Program/<Power>d__0::<>3__exponent
/* Code removed */
}

This stub method instantiates the state machine, sets the initial values for the state machine, and calls the MoveNext method from the state machine <Power>d__0 to start it. In the following section, we will explore in detail how this works in state machine.

State Machine (<Power>d__0)

Listing 9-6 shows the C# compiler-generated state machine <Power>d__0 for the iterator block defined in the Power method of Listing 9-3. The heart of the state machine <Power>d__0 is the MoveNext method, which is used to control the transition of the state. The MoveNext method will do following:

· It maintains the state of the Enumerator.

· It returns an indicator flag to determine whether the iteration is possible, and if not, by returning true when possible and false otherwise. It also sets the Current item from the Enumerator so the consumer of the Enumerator object can get the current item. It changes the state into suspended by setting a state value of 1 and returning to the caller with an indicator flag to indicate the possibility of iteration.

· On the next iteration, the CLR calls the MoveNext method, which loads the previous state of state machine. The CLR changes the state of the state machine from suspended to running and executes the associated code for the iterate item, for example, L_0068 to L_00ac as shown in Listing 9-6. Depending on the looping condition specified in the while statement, it will update the relevant state. This will continue until it finishes the iteration.

Let’s look at the decompiled IL code for the <Power>d__0, as shown in Listing 9-6.

Listing 9-6. Decompiled State Machine <Power>d__0

.class auto ansi sealed nested private beforefieldinit <Power>d__0
extends [mscorlib]System.Object
implements
[mscorlib]System.Collections.Generic.IEnumerable'1<object>,
[mscorlib]System.Collections.IEnumerable,
[mscorlib]System.Collections.Generic.IEnumerator'1<object>,
[mscorlib]System.Collections.IEnumerator, [mscorlib]System.IDisposable
{
{ /*Code removed*/ }
.method public hidebysig specialname rtspecialname instance void
.ctor(int32 <>1__state) cil managed
{ /*Code removed*/ }

.method private hidebysig newslot virtual final instance bool
MoveNext() cil managed
{
.override [mscorlib]System.Collections.IEnumerator::MoveNext
.maxstack 3
.locals init (
[0] bool CS$1$0000,
[1] int32 CS$4$0001,
[2] int32 CS$0$0002,
[3] bool CS$4$0003)
L_0000: ldarg.0
L_0001: ldfld int32 Ch09.Program/<Power>d__0::<>1__state
L_0006: stloc.1
L_0007: ldloc.1

/* Depending on the State of the State machine CLR will
* switch between State 0 and State 1. For the first time or
* first iteration, state of the State machine will be 0,
* The CLR will transfer the program control to the L_0019.*/

L_0008: switch (L_0019, L_0017)
L_0015: br.s L_001b
L_0017: br.s L_0068
L_0019: br.s L_0020
L_001b: br L_00af

/* The CLR start executing from L_0020 to L_0066 while the state
* of the state machine is 0.The CLR will store related information
* in the state machine.*/

L_0020: ldarg.0
L_0021: ldc.i4.m1
L_0022: stfld int32 Ch09.Program/<Power>d__0::<>1__state
L_0027: nop
L_0028: ldarg.0
L_0029: ldc.i4.0
L_002a: stfld int32 Ch09.Program/<Power>d__0::<counter>5__1
L_002f: ldarg.0
L_0030: ldc.i4.1
L_0031: stfld int32 Ch09.Program/<Power>d__0::<result>5__2
L_0036: br.s L_0091
L_0038: nop
L_0039: ldarg.0
L_003a: ldarg.0
L_003b: ldfld int32 Ch09.Program/<Power>d__0::<result>5__2
L_0040: ldarg.0
L_0041: ldfld int32 Ch09.Program/<Power>d__0::number
L_0046: mul
L_0047: stfld int32 Ch09.Program/<Power>d__0::<result>5__2
L_004c: ldarg.0
L_004d: ldarg.0
L_004e: ldfld int32 Ch09.Program/<Power>d__0::<result>5__2
L_0053: box int32
L_0058: stfld object Ch09.Program/<Power>d__0::<>2__current
L_005d: ldarg.0
L_005e: ldc.i4.1
L_005f: stfld int32 Ch09.Program/<Power>d__0::<>1__state
L_0064: ldc.i4.1
L_0065: stloc.0
L_0066: br.s L_00b3

/* The CLR start executing from L_0068 to L_00ac while the state
* of the state machine is 1.*/
L_0068: ldarg.0
L_0069: ldc.i4.m1
L_006a: stfld int32 Ch09.Program/<Power>d__0::<>1__state
L_006f: ldstr "\t{0,9}x{1}\t\u25bc"
L_0074: ldarg.0
L_0075: ldfld int32 Ch09.Program/<Power>d__0::<result>5__2
L_007a: box int32
L_007f: ldarg.0
L_0080: ldfld int32 Ch09.Program/<Power>d__0::number
L_0085: box int32
L_008a: call void
[mscorlib]System.Console::WriteLine(string, object, object)
L_008f: nop
L_0090: nop

/* For State 1, CLR will keep continue to execute the
* IL instruction from L_0091 to L_00ac (while loop) to
* check the whether the <counter>5__1++ < this.exponent
* condition meet*/
L_0091: ldarg.0
L_0092: dup
L_0093: ldfld int32 Ch09.Program/<Power>d__0::<counter>5__1
L_0098: dup
L_0099: stloc.2
L_009a: ldc.i4.1
L_009b: add
L_009c: stfld int32 Ch09.Program/<Power>d__0::<counter>5__1
L_00a1: ldloc.2
L_00a2: ldarg.0
L_00a3: ldfld int32 Ch09.Program/<Power>d__0::exponent
L_00a8: clt
L_00aa: stloc.3
L_00ab: ldloc.3

/* On true transfer the control to the L_0038 */
L_00ac: brtrue.s L_0038

L_00ae: nop
L_00af: ldc.i4.0
L_00b0: stloc.0
L_00b1: br.s L_00b3
L_00b3: ldloc.0
L_00b4: ret
}

.method private hidebysig newslot virtual final instance class
[mscorlib]System.Collections.Generic.IEnumerator'1<object>
System.Collections.Generic.IEnumerable<System.Object>.GetEnumerator()
cil managed {}

.method private hidebysig newslot virtual final instance class
[mscorlib]System.Collections.IEnumerator
System.Collections.IEnumerable.GetEnumerator() cil managed {}

.method private hidebysig newslot virtual final instance void
System.Collections.IEnumerator.Reset() cil managed {}

.method private hidebysig newslot virtual final instance void
System.IDisposable.Dispose() cil managed {}

.property instance object
System.Collections.Generic.IEnumerator<System.Object>.Current
{
.get instance object Ch09.Program/<Power>d__0::
System.Collections.Generic.IEnumerator<System.Object>.get_Current()
}

.property instance object System.Collections.IEnumerator.Current
{
.get instance object Ch09.Program/<Power>d__0::
System.Collections.IEnumerator.get_Current()
}

.field private int32 <>1__state
.field private object <>2__current
.field public int32 <>3__exponent
.field public int32 <>3__number
.field private int32 <>l__initialThreadId
.field public int32 <counter>5__1
.field public int32 <result>5__2
.field public int32 exponent
.field public int32 number
}

Listing 9-6 shows the state machine <Power>d__0 that shows the state controlling is taken care of by the CLR in runtime. In the following section, we will explore the runtime behavior of Listing 9-6 that is taken of care by the CLR.

Before State (0)

In the before state (0), the CLR transfers the program control in L_0020 and executes the instructions up to L_0066 to process the results. In the beginning of this process, the CLR sets the state of the state machine in running state -1 in L_0021 and continues the processing. The CLR checks the state of the looping condition before it starts any further processing by transferring the program control in L_0091 from L_0036. While the CLR executes the instructions, it compares <counter>5__1 with the exponent in L_00a8. On a true return in L_00a8, the CLR moves the program control to the L_0038 from the L_00ac to start processing the result. Otherwise, it transfers the program control in L_00ae to L_00b4 to return from the MoveNext method.

On the other hand, when it finishes the processing to calculate the results, it sets the state of the state machine to suspended state (1) in L_005e and in L_0066, and the CLR again transfers the control to the L_00b3, which returns from the MoveNext method to terminate this round of iteration.

Figure 9-5 will help you understand the state transition of <Power>d__0 in more depth.

images

Figure 9-5. Iterator internal

Suspended State (1)

On the next iteration, when CLR calls the MoveNext method, it will determine that the state of the state machine is suspended (1), which was set earlier. The CLR executes the code block defined for suspended state (1), which is in L_0068. In the following instruction of L_0068, the CLR changes the state of state machine to running (-1) in L_0069. It starts processing with the result processed in the previous iteration from L_006a to L_0090. The CLR does the following processing and produces the following result:

Console.WriteLine("\t{0,9}x{1}\t\u25BC", result, number);

After finishing it, the CLR continues the execution and comes to L_0091. In between L_0091 to L_00ac, the CLR will check the loop condition as to whether or not it is possible to iterate through the iterator. In L_00a8, the clt instruction compares the <counter>5__1 with the exponent. On return of true, the CLR moves the program control to the L_0038 from L_00ac to process the next iterated item. The CLR stores the result in the state machine for the next processing and sets the state of the state machine back into suspended (1) in L_0064. It returns from the MoveNext method to the caller from L_0066.

Images Note:ldc.i4.m1: Push -1 onto the stack as int32.
ldc.i4.1: Push 1 onto the stack as int32.
Clt: Push 1 if value1 is less than value2, or else push 0.

Examine Memory of the State Machine

Figure 9-6 presents the memory information captured while debugging the executable produced from Listing 9-3 using the windbg.exe tool. It shows the state of the <Power>d__0 state machine while it iterates through the enumerator in the foreach statement used in the Main method and the stack information of the Main method of each iteration.

The following command is run in the windbg.exe tool to set the breakpoint while running the executable code of Listing 9-3.

!bpmd Ch09.exe Ch09.Program.Main
!bpmd Ch09.exe Ch09.Program.Power
!bpmd Ch09.exe Ch09.Program+<Power>d__0.MoveNext
!g

The breakpoint is being set following the Run command in the windbg.exe tool on each iteration to get the state of the state machine <Power>d__0 and Main method.

!clrstack -a
/* 0x018a4224 is the address of the <Power>d__0 instance */
!dumpobj 0x018a4224
p

Let’s look at the captured memory information of the executable of Listing 9-3, as shown in Figure 9-6.

images

Figure 9-1. Memory information while debugging Listing 9-3 using the windbg.exe tool

Figure 9-6 illustrates that on every iteration except for iteration 1 and 2, CLR sets the state if the State machine 1 is suspended. Also, following every iteration the CLR uses an instance of the <Power>d__0 to store the state of the state machine.

Summary

In this chapter we have learned about the usage of the foreach statement and iterators block through a sample program. The C# compiler wraps the code block used in the foreach statement using a try catch block. It iterates through the collection based on a boolean indicator return from the MoveNextmethod of the enumerator object. You also learned how the state machine the C# compiler generates for the iterator block maintains the state of the iteration while iterating a collection. In the next chapter we will explore the string data type.