Iterator Pattern C#

Contents

What Is Iterator Pattern?

The Iterator pattern provides a way of accessing elements of a collection sequentially, without knowing how the collection is structured.

The idea is that an aggregate object such as an array or list will give you a way to access its elements without exposing its internal structure.

Moreover, you might want to traverse the list in different ways, depending on what you want to accomplish. But you probably don’t want to bloat the List interface with operations for different traversals, even if you could anticipate the ones you will need. You might also need to have more than one traversal pending on the same list.

The Iterator pattern lets you do all this. The key idea in this pattern is to take the responsibility for access and traversal out of the list object and put it into an iterator object.

Iterator Design Pattern Example

In the traditional design pattern approach, iterator pattern has an Aggregate interface for creating an Iterator object and the Iterator interface for traversing an Aggregate object.

Let’s see a quick example,

 1 class Program
 2 {
 3     public class Weeks //Aggregate object
 4     {
 5         private string[] weeks = new string[]{
 6             "Monday",
 7             "Tuesday",
 8             "Wednesday",
 9             "Thursday",
10             "Friday",
11             "Saturday",
12             "Sunday"
13             };
14 
15         public IWeeksIterator GetWeeksIterator()
16         {
17             //creates an Iterator object
18             return new WeeksIterator(weeks);
19         }
20     }
21 
22     public interface IWeeksIterator //Iterator interface
23     {
24         string Current { get; }
25 
26         bool MoveNext();
27     }
28 
29     public class WeeksIterator : IWeeksIterator //Iterator object
30     {
31         private readonly string[] weeks;
32         private int position;
33 
34         public WeeksIterator(string[] weeks)
35         {
36             this.weeks = weeks;
37             this.position = -1;
38         }
39 
40         public string Current => weeks[position];
41 
42         public bool MoveNext()
43         {
44             if (++position == weeks.Length) return false;
45             return true;
46         }
47     }
48 
49     static void Main(string[] args)
50     {
51         var weeks = new Weeks();
52         var iterator = weeks.GetWeeksIterator();
53         while (iterator.MoveNext())
54         {
55             Console.WriteLine(iterator.Current);
56         }
57         Console.ReadLine();
58     }
59 }

In the above code, we have created a collection class Weeks which is our Aggregate object, for the sake of simplicity we have not created an Aggregate interface, our Weeks class contains a private array of the string containing days in a week. Next, we have a WeeksIterator class, implementing the IWeeksIterator which is our Iterator interface, that traverse over this collection.

A sharp reader will wonder why we did not just loop through weeks. Like this,

 1 class Program
 2 {
 3     public class Weeks
 4     {
 5         private string[] weeks = new string[]{
 6             "Monday",
 7             "Tuesday",
 8             "Wednesday",
 9             "Thursday",
10             "Friday",
11             "Saturday",
12             "Sunday"
13             };
14 
15         public string[] Days => weeks;
16     }
17 
18     static void Main(string[] args)
19     {
20         var days = new Weeks().Days;
21         for (int i = 0; i < days.Length; i++)
22         {
23             Console.WriteLine(days[i]);
24         }
25     }
26 }

The reason is that weeks is declared not in the Client, but in its own collection class. Even without the benefits gained from separating iteration and enumeration, it would be a bad style for the Client to access weeks directly.

However, talking about benefits,

Iterators can also provide filters, transformations, and projections on the data.

Let’s say we just want weekdays (all days of the week other than Sunday or Saturday) from the collection, in that case, we can create another iterator.

 1 public class WeekDaysIterator : IWeeksIterator
 2 {
 3     private readonly string[] weeks;
 4     private int position;
 5 
 6     public WeekDaysIterator(string[] weeks)
 7     {
 8         this.weeks = weeks;
 9         this.position = -1;
10     }
11 
12     public string Current => weeks[position];
13 
14     public bool MoveNext()
15     {
16         if (++position == (weeks.Length -2)) return false;
17         return true;
18     }
19 }

Here only change I did is, (weeks.Length -2) in MoveNext method that just skips the last two values in the array that is Saturday and Sunday!

C# IEnumerator

Iterators (also known as Enumerators) are responsible for producing the next element in a sequence defined by certain criteria. Such a sequence is said to be Enumerable. For example, the next Fibonacci number in the series. The iterator/enumerator is the means by which we loop over this sequence of elements from beginning to end.

From the early days of C# 1.0 and 2.0, C# supported the iterators. In C#, iterators are defined using the IEnumertor interface consisted of the methods for the basic elements of a loop: Current, MoveNext, and Reset. Similar to the interface we mentioned above.

Let’s transform our above code to use C# IEnumertor. You will find the IEnumertor interface in System.Collections namespace

 1 class Program
 2 {
 3     public class Weeks
 4     {
 5         ...
 6         ...
 7     }
 8 
 9     public class WeeksIterator : IEnumerator
10     {
11         private readonly string[] weeks;
12         private int position;
13 
14         public WeeksIterator(string[] weeks)
15         {
16             this.weeks = weeks;
17             this.position = -1;
18         }
19 
20         public object Current => weeks[position];
21 
22         public bool MoveNext()
23         {
24             if (++position == weeks.Length) return false;
25             return true;
26         }
27 
28         public void Reset()
29         {
30             this.position = -1;
31         }
32     }
33 
34     public class WeekDaysIterator : IEnumerator
35     {
36         private readonly string[] weeks;
37         private int position;
38 
39         public WeekDaysIterator(string[] weeks)
40         {
41             this.weeks = weeks;
42             this.position = -1;
43         }
44 
45         public object Current => weeks[position];
46 
47         public bool MoveNext()
48         {
49             if (++position == (weeks.Length - 2)) return false;
50             return true;
51         }
52 
53         public void Reset()
54         {
55             this.position = -1;
56         }
57     }
58 
59     static void Main(string[] args)
60     {
61         ...
62         ...
63     }
64 }

Now the only thing we changed here is that instead of custom IWeeksIterator interface we use the .NET IEnumerator interface which has the same methods define as we defined for the IWeeksIterator. Now, the IEnumerator interface use object as the return type for Current property in the interface. Hence, we need to change the return type of our Current property as well:

1 public object Current => weeks[position];

Now, you would be wondering how this could be of any use at all. We can just use the IWeeksIterator or any custom interface. The real benefit is that .NET IEnumerator has some language support for the iterator pattern. Before that let’s talk about the IEnumerable interface.

C# IEnumerable

IEnumerable is an interface, in System.Collections namespace, defining a single method GetEnumerator() that returns an IEnumerator - the same interface we use to implement our iterators.

Now, IEnumerable acts as an Aggregate interface that guarantees to return an iterator.

1 public interface IEnumerable
2 {
3     IEnumerator GetEnumerator();
4 }

The thing is all collections in the .NET library implement IEnumerable (i.e., they each provide a conforming GetEnumerator method).

And,

The foreach statement in C# is a syntactic sugar that hides from you that you are using the GetEnumerator and MoveNext methods.

So,

By implementing the IEnumerable interface in an iterator (or any collection class) you can use them in a foreach loop. Let’s see how,

 1 class Program
 2 {
 3     public class Weeks
 4     {
 5         ...
 6         ...
 7 
 8         public IEnumerable GetWeeksIterator()
 9         {
10             return new WeeksIterator(weeks);
11         }
12 
13         public IEnumerable GetWeekDaysIterator()
14         {
15             return new WeekDaysIterator(weeks);
16         }
17     }
18 
19     public class WeeksIterator : IEnumerator, IEnumerable
20     {
21         ...
22         ...
23 
24         public IEnumerator GetEnumerator()
25         {
26             return this;
27         }
28     }
29 
30     public class WeekDaysIterator : IEnumerator, IEnumerable
31     {
32         ...
33         ...
34 
35 
36         public IEnumerator GetEnumerator()
37         {
38             return this;
39         }
40     }
41 
42     static void Main(string[] args)
43     {
44         var weeks = new Weeks();
45         foreach (var item in weeks.GetWeeksIterator())
46         {
47             Console.WriteLine(item);
48         }
49         Console.ReadLine();
50     }
51 }

The only thing I changed in the above code is that I implemented the IEnumerable interface in the iterators and return this object. And the other important thing we need to do is change the return type for GetWeeksIterator() and GetWeekDaysIterator() method, they need to return IEnumerable instead of IEnumerator as foreach statement looks for an IEnumerable (which ensures an IEnumerator is returned by the passed collection).

Still not a major benefit right? What if I tell you that, you can super simplify the above code with the yield keyword.

C# Yield

“Life is really simple, but we insist on making it complicated.” ~ Confucius

Yield is a special keyword that can be used only in the context of iterators. It instructs the compiler to convert this regular code to a state machine (an enumerator). The auto-generated code keeps track of where you are in the collection and it implements methods such as MoveNext and Current.

Yeah! that means we can write less and do more, i.e. we can remove the WeeksIterator and WeekDaysIterator class. Let’s see how

 1 class Program
 2 {
 3     public class Weeks
 4     {
 5         private string[] weeks = new string[]{
 6             "Monday",
 7             "Tuesday",
 8             "Wednesday",
 9             "Thursday",
10             "Friday",
11             "Saturday",
12             "Sunday"
13             };
14 
15         public IEnumerable GetWeeksIterator()
16         {
17             foreach (var item in weeks)
18             {
19                 yield return item;
20             }
21         }
22 
23         public IEnumerable GetWeekDaysIterator()
24         {
25             for (int i = 0; i < (weeks.Length - 2); i++)
26             {
27                 yield return weeks[i];
28             }
29         }
30     }
31 
32     static void Main(string[] args)
33     {
34         var weeks = new Weeks();
35         foreach (var item in weeks.GetWeeksIterator())
36         {
37             Console.WriteLine(item);
38         }
39         Console.ReadLine();
40     }
41 }

Using yield to define an iterator removes the need for an explicit extra class (the class that holds the state for an enumeration, WeeksIterator, and WeekDaysIterator in our case!) when you implement the IEnumerable and IEnumerator pattern for a custom collection type.

Also, remember that you can write LINQ queries in C# for any collection of objects that supports IEnumerable or the generic IEnumerable<T> interface. So that’s the ultimate benefit you can get by implementing these interfaces.

Note: You can learn more about Generic Types and Generic IEnumerable in my blog post Generics In C#.

Below is the final version of the Weeks class. Here we have implemented the IEnumerable interface in the Weeks class and used the yield keyword in the GetEnumerator method to define our iterator. Now, we can directly use weeks object in a foreach loop in the client code.

 1 class Program
 2 {
 3     public class Weeks : IEnumerable
 4     {
 5         private string[] weeks = new string[]{
 6             "Monday",
 7             "Tuesday",
 8             "Wednesday",
 9             "Thursday",
10             "Friday",
11             "Saturday",
12             "Sunday"
13             };
14 
15 
16         public IEnumerator GetEnumerator()
17         {
18             foreach (var item in weeks)
19             {
20                 yield return item;
21             }
22         }
23     }
24 
25     static void Main(string[] args)
26     {
27         var weeks = new Weeks();
28         foreach (var item in weeks)
29         {
30             Console.WriteLine(item);
31         }
32         Console.ReadLine();
33     }
34 }

Where To Apply Iterator Pattern?

  • When you want to access a collection of objects without exposing its internal representation.

  • When there are multiple traversals of objects need to be supported in the collection.

Note: You can download the complete solution demo from my github repository.

Further Reading

  • Lazy LINQ and Enumerable Objects by K. Scott Allen - In this blog post, Scott explains why LINQ operators return an IEnumerable instead of something more useful, like a List, and explains the Lazy Loading behavior of Enumerators.

  • Coroutines In Unity - Coroutine is a Unity type that is used to create parallel actions returning an IEnumerator to do so. A coroutine is a Unity Engine class while IEnumerator belongs to the .NET. Knowing how the Unity engine uses IEnumerator will give you a different perspective to see an Enumerator.

  • The cost of enumerating in .NET by Joe Duffy - In this post, Joe discusses the enumeration pattern in .NET concerning performance and implies how enumeration pattern cause some overhead that makes it difficult to compete with ordinary for loops.

  • Async Streams with C# 8 by Christian Nagel - One of the many great features of C# 8 is async streams. Before C# 8, you could use the await keyword only to get a single result – when the asynchronous method returns the result. This changes with C# 8. Using await it’s now possible to get a stream of results. This was made possible by defining asynchronous iterator interfaces, and updates to the foreach loop and the yield statement. This article gives you more information about this!

Iterator Pattern C#
Share this

Subscribe to Code with Shadman