Iterators

An iterator is a block of code that supplies all the values to be used in a foreach loop.  A class that represents a collection can implement the System.Collections.IEnumerable interface. This interface requires an  implementation for the GetEnumerator()method  which returns an IEnumerator interface. The IEnumerator interface has a Current property which contains the current value that was returned by  the iterator. It also has a MoveNext() method  which moves the Current property to the next item and returns false if there is  no more item left. The Reset() method returns the  iterator back to the first item. The IEnumerator  interface is implemented by different types of collections in the .NET library  and this includes arrays which is why you can use the foreach loop on them. Supposed we have a code like this which reads every  element of an array using a foreach loop.

int[] numbers = { 1, 2, 3, 4, 5 };

foreach(int n in numbers)
{
    Console.WriteLine(n);
}

To better understand iterators, let’s translate the above foreach loop into a call to an array’s GetEnumerator() method.

int[] numbers = { 1, 2, 3, 4, 5 };

IEnumerator iterator = numbers.GetEnumerator();

while(iterator.MoveNext())
{
    Console.WriteLine(iterator.Current);
}

As you can see, we first retrieve the array’s iterator using the GetEnumerator() method which returns an IEnumerator interface. We used this iterator in a while loop and called the MoveNext() method. The MoveNext() method  retrieves the first element of a collection such as an array and if it is  successful in retrieving, the method will return true.  It will then retrieve the second element on the next call and so on until it  reaches the end of the array where it will return false  because there is no more to retrieve. The retrieved value of the element can be  accessed using the IEnumerator.Current property.

Now that you know how an iterator works and its major contribution when using  iterating values from collections, let’s consider creating our own iterators.  Using an iterator requires a yield return  statement. A yield return statement is different  from an ordinary return statement. One visible  difference is the use of the keyword yield before  the return keyword. Consider the following  example:

using System;
using System.Collections;

namespace IteratorsDemo
{
    class Program
    {
        public static IEnumerable GetMessages()
        {
            yield return "Message 1";
            yield return "Message 2";
            yield return "Message 3";
        }

        public static void Main()
        {
            foreach (string message in GetMessages())
            {
                Console.WriteLine(message);
            }
        }
    }
}

Example 1 – A Simple Iterator

Message 1
Message 2
Message 3

The GetMessages() method returns an IEnumerable object which in turn contains a  definition for a GetEnumerator() method. The yield return statement brings the value it will  return to the variable that will hold each of the values in a foreach loop.  The first yield return statement in the method is  returned to the foreach loop inside our Main() method. When the foreach loop calls again the GetMessages() method, then the next yield return  statement is returned to it. This continues until no more yield returnstatement is found. You can interrupt the returning of values from the method by using the yield break  statement.

public static IEnumerable GetMessages()
{
    yield return "Message 1";
    yield return "Message 2";
    yield break; 
    yield return "Message 3";
}

Creating Our Own Iterator


Let’s take a look an example of using an iterator by creating a new class  which contains an ArrayList field that will  hold some values. We will then create an iterator which will provide a foreach loop with the values from the ArrayList field.

using System.Collections;
using System;

namespace IteratorsDemo2
{
    public class Names : IEnumerable              
    {
        private ArrayList innerList;

        public Names(params object[] names)
        {
            innerList = new ArrayList();

            foreach (object n in names)
            {
                innerList.Add(n);
            }
        }

        public IEnumerator GetEnumerator()  
        {                                   
            foreach (object n in innerList) 
            {                               
                yield return n.ToString();  
            }                               
        }                                   
    }

    public class Program
    {
        public static void Main()
        {
            Names nameList = new Names("John", "Mark", "Lawrence", "Michael", "Steven");

            foreach (string name in nameList) 
            {                                 
                Console.WriteLine(name);      
            }                                 
        }
    }
}

Example 2 – User Defined Iterator

We created a collection class named Names which  will hold a list of names. The definition of the class in line 6 shows that we  did not implement the CollectionBase class. This  is because the CollectionBase class implements the IEnumerable interface and already has an  implementation of the GetEnumerator() method. We  are creating our collection class from scratch and we will define our own  iterator. Our class simply implements the IEnumerable  interface which now requires our class to have an implementation of the GetEnumerator() method. Our own iterator is  defined in lines 20-26. Inside it, we iterate through each of the value from the innerList field. Each value is converted to string  and then yield returned to the caller. Lines 35-38 shows our own iterator in  work. Since the yield returns in our iterator convert each value to string, we  can simply use string as the type of the range variable of the foreach loop. When the next value will be  retrieved from the iterator via the foreach loop  in our Main() method, the foreach loop inside the iterator goes to the next iteration and yield  return the next value from the innerList. Without  the iterator, we won’t be able to use foreach loop  for our Names class.

Creating our own iterator gives us a great control on the behavior of the foreach loop when dealing with our class. For  example, we can edit our iterator to only return names starting with letter M.

public IEnumerator GetEnumerator()
{
    foreach (object n in innerList)
    {
        if (n.ToString().StartsWith("M"))
            yield return n.ToString();   
    }
}

We used the StartsWith() method of the System.String class to determine if a name starts  with letter M. If the names start with letter M, then we yield return it. If  not, then it is ignored and then next name in the innerList is inspected. With our modified GetEnumerator() method, when you use a foreach loop on an instance of the Names class,  only names starting with letter M will be retrieved.

Even if you can do the above technique of modifying GetEnumerator() method, it would be more practical to just define a  separate method for retrieving names starting with a specified letter.

public IEnumerable GetNamesStartingWith(string letter)
{
    foreach (object n in innerList)
    {
        if (n.ToString().StartsWith(letter))
            yield return n.ToString();
    }
}

We now have a more flexible iterator that you can use to retrieve names which  start with a specified letter or substring. Note that we used IEnumerable instead of IEnumerator. The rule is use IEnumerable for all iterators except the GetEnumerator()  method which is used as default by the foreach loop. The IEnumerable already has a GetEnumerator() method. When you call this  iterator, you need to modify our foreach loop in lines 35-38.

foreach (string name in nameList.GetNamesStartingWith("M"))
{
    Console.WriteLine(name);
}

Improving Our Animals Dictionary Class


A more practical use of an iterator is applying it to a collection or dictionary class. For example, the dictionary class we made in the  last lesson uses DictionaryEntry as the type of variable that will store each of the elements inside a  foreach loop. By using an iterator, we can use Animal as the type of the class and save as a little casting. The following code demonstrates this to you.

using System.Collections;
using System;

namespace IteratorsDemo3
{
    public class Animal
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public double Height { get; set; }

        public Animal(string name, int age, double height)
        {
            Name = name;
            Age = age;
            Height = height;
        }
    }

    public class Animals : DictionaryBase
    {
        public void Add(string key, Animal newAnimalnewAnimal)
        {
            Dictionary.Add(key, newAnimalnewAnimal);
        }

        public void Remove(string key)
        {
            Dictionary.Remove(key);
        }

        public Animal this[string key]
        {
            get { return (Animal)Dictionary[key]; }
            set { Dictionary[key] = value; }
        }

        public new IEnumerator GetEnumerator()           
        {                                                
            foreach (object animal in Dictionary.Values) 
            {                                            
                yield return (Animal)animal;             
            }                                            
        }                                                
    }

    public class Program
    {
        public static void Main()
        {
            Animals animalDictionary = new Animals();

            animalDictionary.Add("Animal1", new Animal("John", 10, 100));
            animalDictionary.Add("Animal2", new Animal("Sussy", 5, 10));
            animalDictionary.Add("Animal3", new Animal("Frank", 3, 5));
            animalDictionary.Add("Animal4", new Animal("Mark", 7, 15));

            foreach (Animal animal in animalDictionary) 
            {                                           
                Console.WriteLine(animal.Name);         
            }                                           
        }
    }
}

Example 3 – Adding an Iterator to a Custom Dictionary

Lines 38-44 defines an iterator for our Animals dictionary class. The DictionaryBase class  already implements the IEnumerableinterface which has the GetEnumerator() method that is used for getting values from a collection using a foreach loop. We implement our own GetEnumerator() method. Notice that it has a return type of IEnumerator.The new keyword simply indicates that the program should use this version of GetEnumerator() instead of the one already defined in the  DictionaryBase. Inside the method, we use a foreach loop  to cast and yield return each of the Animal objects from the Values properties.  Recall that without an iterator, using a foreach loop for our dictionary class  looks like this:

foreach (DictionaryEntry animal in animalDictionary)
{
    Console.WriteLine((animal.Value as Animal).Name);
}

With the new iterator defined in our dictionary class, you can now use  the type of each element in a foreach loop without using the DictionaryEntry class.

foreach (Animal animal in animalDictionary)
{
    Console.WriteLine(animal.Name);
}

Each iteration of the above foreach loop triggers an iteration of the foreach loop inside our iterator and each loop executes a yield return statement.

Written by compitionpoint

Leave a Comment

Your email address will not be published. Required fields are marked *