Implementation example. This section describes a possible implementation of iterators in terms of standard C# constructs
This section describes a possible implementation of iterators in terms of standard C# constructs. The implementation described here is based on the same principles used by the Microsoft C# compiler, but it is by no means a mandated implementation or the only one possible. The following Stack<T> class implements its GetEnumerator method using an iterator. The iterator enumerates the elements of the stack in top to bottom order. using System; class Stack<T>: IEnumerable<T> public void Push(T item) { public T Pop() { public IEnumerator<T> GetEnumerator() { The GetEnumerator method can be translated into an instantiation of a compiler-generated enumerator class that encapsulates the code in the iterator block, as shown in the following. class Stack<T>: IEnumerable<T> public IEnumerator<T> GetEnumerator() { class __Enumerator1: IEnumerator<T>, IEnumerator public __Enumerator1(Stack<T> __this) { public T Current { object IEnumerator.Current { public bool MoveNext() { public void Dispose() { void IEnumerator.Reset() { In the preceding translation, the code in the iterator block is turned into a state machine and placed in the MoveNext method of the enumerator class. Furthermore, the local variable i is turned into a field in the enumerator object so it can continue to exist across invocations of MoveNext. The following example prints a simple multiplication table of the integers 1 through 10. The FromTo method in the example returns an enumerable object and is implemented using an iterator. using System; class Test static void Main() { The FromTo method can be translated into an instantiation of a compiler-generated enumerable class that encapsulates the code in the iterator block, as shown in the following. using System; class Test static IEnumerable<int> FromTo(int from, int to) { class __Enumerable1: public __Enumerable1(int __from, int to) { public IEnumerator<int> GetEnumerator() { IEnumerator IEnumerable.GetEnumerator() { public int Current { object IEnumerator.Current { public bool MoveNext() { public void Dispose() { void IEnumerator.Reset() { The enumerable class implements both the enumerable interfaces and the enumerator interfaces, enabling it to serve as both an enumerable and an enumerator. The first time the GetEnumerator method is invoked, the enumerable object itself is returned. Subsequent invocations of the enumerable object’s GetEnumerator, if any, return a copy of the enumerable object. Thus, each returned enumerator has its own state and changes in one enumerator will not affect another. The Interlocked.CompareExchange method is used to ensure thread-safe operation. The from and to parameters are turned into fields in the enumerable class. Because from is modified in the iterator block, an additional __from field is introduced to hold the initial value given to from in each enumerator. The MoveNext method throws an InvalidOperationException if it is called when __state is 0. This protects against use of the enumerable object as an enumerator object without first calling GetEnumerator. The following example shows a simple tree class. The Tree<T> class implements its GetEnumerator method using an iterator. The iterator enumerates the elements of the tree in infix order. using System; class Tree<T>: IEnumerable<T> public Tree(T value, Tree<T> left, Tree<T> right) { public IEnumerator<T> GetEnumerator() { class Program static Tree<T> MakeTree<T>(params T[] items) { // The output of the program is: static void Main() { Tree<string> strings = MakeTree( The GetEnumerator method can be translated into an instantiation of a compiler-generated enumerator class that encapsulates the code in the iterator block, as shown in the following. class Tree<T>: IEnumerable<T> public IEnumerator<T> GetEnumerator() { class __Enumerator1: IEnumerator<T>, IEnumerator public __Enumerator1(Node<T> __this) { public T Current { object IEnumerator.Current { public bool MoveNext() { case 0: case 1: __left_dispose: __yield_value: case 2: case 3: __right_dispose: __end: } public void Dispose() { case 1: case 3: } void IEnumerator.Reset() { The compiler generated temporaries used in the foreach statements are lifted into the __left and __right fields of the enumerator object. The __state field of the enumerator object is carefully updated so that the correct Dispose() method will be called correctly if an exception is thrown. Note that it is not possible to write the translated code with simple foreach statements.
|