Section 1.4. Enumerate Using Generic Iterators


1.4. Enumerate Using Generic Iterators

In the previous examples you could not iterate over your list of Pilgrims using a foreach loop. As such, if you try to use the following code in Example 1-3:

foreach ( Pilgrim p in pilgrims ) {    Console.WriteLine("The pilgrim's name is " + p.ToString( )); }

you will receive the following error:

Error      1      foreach statement cannot operate on variables of type  'ImplementingGenericInterfaces.LinkedList <ImplementingGenericInterfaces.Pilgrim>'  because 'ImplementingGenericInterfaces.LinkedList <ImplementingGenericInterfaces. Pilgrim>' does not contain a public definition for 'GetEnumerator'

In earlier versions of C#, implementing GetEnumerator was somewhat complicated and always tedious, but in C# 2.0 it is greatly simplified.


Note: Adding iterators allows a client to iterate over your class using foreach.

1.4.1. How do I do that?

To simplify the process of creating iterators, we'll begin by simplifying both the Pilgrim class and the Linked List class. The Linked List class will forgo all use of nodes and will store its contents in a fixed-size array (as the simplest type-safe container imaginable). Thus, it is a Linked List in name only! This will allow us to focus on the implementation of the IEnumerator interface, as shown in Example 1-4.

Example 1-4. Implementing IEnumerator, simplified
#region Using directives     using System; using System.Collections.Generic; using System.Text;     #endregion     namespace SimplifiedEnumerator {    // simplified Pilgrim    public class Pilgrim     {       private string name;       public Pilgrim(string name)       {          this.name = name;       }       public override string ToString( )       {          return this.name;       }        }        //  simplified Linked List    class NotReallyALinkedList<T> : IEnumerable<T>    {       // the entire linked list is stored in this       // fixed size array       T[  ] myArray;           // constructor takes an array and stores the members       public NotReallyALinkedList(T[  ] members)       {          myArray = members;       }              // implement the method for IEnumerable       IEnumerator<T> IEnumerable<T>.GetEnumerator( )       {          foreach (T t in this.myArray)          {             yield return t;          }       } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator( ) {    throw new NotImplementedException( ); }    }            class Program    {       static void Main(string[  ] args)       {          // hardcode a string array of Pilgrim objects          Pilgrim[  ] pilgrims = new Pilgrim[5];          pilgrims[0] = new Pilgrim("The Knight");          pilgrims[1] = new Pilgrim("The Miller");          pilgrims[2] = new Pilgrim("The Reeve");          pilgrims[3] = new Pilgrim("The Cook");          pilgrims[4] = new Pilgrim("The Man Of Law");              // create the linked list, pass in the array          NotReallyALinkedList<Pilgrim> pilgrimCollection =              new NotReallyALinkedList<Pilgrim>(pilgrims);              // iterate through the linked list          foreach (Pilgrim p in pilgrimCollection)          {             Console.WriteLine(p);          }       }    } }

Output:

The Knight The Miller The Reeve The Cook The Man Of Law

1.4.2. What just happened?

In this example, the linked list is greatly simplified to keep its members in an array (in fact, it is not really a linked list at all). Because you've made your pseudo-LinkedList enumerable, however, now you can enumerate the Pilgrims in the pilgrims collection using a foreach loop.

When you write:

foreach (Pilgrim p in pilgrimCollection)

the C# compiler invokes the GetEnumerator method of the class. Internally, it looks more or less like this:

Enumerator e = pilgrimCollection.GetEnumerator( ); while (e.MoveNext( )) {   Pilgrim p = e.Current; }

As noted earlier, in C# 2.0 you do not have to worry about implementing MoveNext( ) or the current property. You need only use the new C# keyword yield.


Note: Whenever you call foreach, the compiler internally translates it to a call to GetEnumerator.You use yield only in iterator blocks. It either provides a value to the enumerator object or it signals the end of the iteration:
yield return expression; yield break;

If you step into the foreach loop with the debugger, you'll find that each time through the foreach loop, the GetEnumerator method of the linked list is called, and each time through the next member in the array, it is yielded back to the calling foreach loop.

1.4.3. What about...

...implementing the GetEnumerator method on a more complex data structure, such as our original LinkedList?

That is shown in the next lab.

1.4.4. Where can I learn more?

For more on this subject, see the extensive article in MSDN titled "Iterators (C#)."



Visual C# 2005(c) A Developer's Notebook
Visual C# 2005: A Developers Notebook
ISBN: 059600799X
EAN: 2147483647
Year: 2006
Pages: 95
Authors: Jesse Liberty

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net