The Iterator design pattern provides us with a common method of enumerating a list of items or array, while hiding the details of the list’s implementation. This provides a cleaner use of the array object and hides unneccessary information from the client, ultimately leading to better code-reuse, enhanced maintainability, and fewer bugs. The iterator pattern can enumerate the list of items regardless of their actual storage type.
The Iterator design pattern is actually a quite common pattern in most object oriented programming languages, especially in C# and Java. In fact, both languages include their own pre-built Iterator pattern, which can be inherited from. Of course, knowing how to create your own Iterator design pattern can provide great benefits and allow you to expand your design when needed.
This article will describe three ways to traverse a list of items, from simply walking the array, to using the built-in C# ASP .NET IEnumerable interface, to finally creating your own home-grown Iterator design pattern.
We’ll start this article off with a class, which holds internal data in the form of a list. In this particular case, the list will be an array of objects. Specifically, we’ll use the C# .NET System.Collections.Generic type List<> as our array. It is important to note that any form of array structure can be used to hold the internal data, since we’ll be creating an Iterator to enumerate the items, regardless of type.
Let’s begin by creating a class called GoldArray. GoldArray holds an internal list of items of the type ItemContents. An ItemContents object simple has a name and description. Our GoldArray class manages this list of ItemContents by allowing us to Get, Add, and Remove objects from its internal list. While our internal list happens to be of type List<ItemContents>, it could certainly be any other collection type, such as a Hashtable<key, ItemContents>, a basic array ItemContents, etc. The class is defined as follows:
The ItemContents object is a simple C# ASP .NET class defined as follows:
To traverse the items in our GoldArray class, we can simply walk the array of ItemContents objects that exist in the internal array by calling GoldArray.GetItems() to get the list. This can be done as follows:
As you can see in the ouput, by calling GetItems() to access the internal list, we can easily traverse the array using the C# .NET foreach command. While this certainly works, the problem with this method is that we’re exposing GoldArray’s internal data structures, private data which should really only be accessed by itself. Through the GetItems() function, we can easily tell that GoldArray is using a List
We can improve the design above by creating our own Iterator design pattern, which will allow us to enumerate the items in GoldArray without exposing the internal implementation details. This is the perfect way to decouple GoldArray’s internal data from the client code. To do this, we’ll first need a common interface:
This is the standard interface definition for the Iterator design pattern. We simply define a HasNext() function, which tells us if more item exists, and a Next() function, which returns the next object. Note, since we define Next() as returning an object, we’ll need to cast the resulting value to our desired type while enumerating. However, this has the added advantage of code-reuse, as we can re-use this interface for any type of class.
To put our new ICustomIterator interface to use, we’ll first need to create an implementation of the interface for our GoldArray class. We’ll call this GoldArrayIterator1 (the 1 will differentiate this iterator implementation from the one we’ll make using .NET’s built-in iterator design pattern a bit later).
The above iterator implementation begins by holding a private list of ItemContents, just like the GoldArray class does. This list is initialized in the constructor so that it holds a copy of the items from GoldArray. We then fill in the body definitions for the interface. As you can see in the code above, HasNext() simply checks if the internal index is less than the total items in the list. The Next() function simply returns the item at the current position and increments the position. These two functions allow us to traverse the array. It’s important to note that since our iterator class implements the ICustomIterator interface, our client code can refer to ICustomIterator and never need to know the details behind the GoldArrayIterator1 class itself, or GoldArray’s internal data. This effectively decouples the client code from the objects.
We now need to add one more function to our GoldArray class so that it can provide us with an ICustomIterator to enumerate its data, as follows:
The new function CreateIterator(), simple returns an implementation of the ICustomIterator interface and initializes it with a copy of GoldArray’s internal list. We can actually remove the GetItems() function from GoldArray, since we no longer need it.
We can now enumerate the list of items in our client code, completely de-coupled from GoldArray, as shown in the following example:
Notice how we ask GoldArray to create an iterator for us. We then use the iterator in a while loop, checking the HasNext() function each time. Our only reference is to the generic C# .NET interface ICustomIterator. At this point, you should be able to see how we could create a variety of classes which implement ICustomIterator and could be enumerated in the exact same way. In fact, this is exactly how C# ASP .NET’s own built-in iterator design pattern, IEnumerable, works.
In the code samples above, we’ve learned how to create our very own custom iterator design pattern to enumerate a list of items. This is an important pattern to know, should you find yourself in a programming language without a default implementation. However, C# ASP .NET happens to include its own iterator in the IEnumerable interface. This interface uses another interface, IEnumerator, to traverse a list. To use C#’s version of the iterator design pattern, we’ll need to create an implementation of the IEnumerable interface, just like we did for our own ICustomIterator interface. We’ll call this new iterator class, GoldArrayIterator2:
While the code looks a bit more complicated, it’s actually pretty simple, and very similar to our own ICustomIterator design pattern. We still carry an internal copy of the list of items, defined as _itemContents. We also still carry an index pointer into the array, to indicate our current position. Our constructor is the same, in that it copies the list of items from GoldArray. The main difference is really just the names of the functions. Instead of our custom iterator’s Next() function, which returned an object and advanced the index, C# provides a separate function MoveNext() just for advancing the index, and another function Current, for accessing the current object. Our custom iterator class combined the two in a single Next() function.
Just as we did with our ICustomIterator, we still need to define a CreateIterator() function in the GoldArray class, except the naming in C# .NET is GetEnumerator(), as follows:
The new changes to our GoldArray class mainly include implementing the IEnumerable interface. This requires us to define a GetEnumator() function, just as we did with our own custom iterator pattern. Instead of returning a new GoldArrayIterator1(list) object, we now return a new GoldArrayIterator2(list) object.
As with our custom iterator pattern, we can remove the GetItems() function from GoldArray, since we no longer need it.
The real beauty of using C# ASP .NET’s built-in iterator design pattern comes when using it in the client code. We can take advantage of C# .NET’s foreach command to enumerate the list of items, without accessing the internal details (ie. avoiding tight coupling of classes), as follows:
You may notice that the above code looks very similar to our initial starting code example at the top of this article, which looked like the following:
The key difference between these two code segements is that one uses an iterator and one does not. The code directly above, which doesn’t use an iterator, is directly coupled with GoldArray in that it calls GetItems() and has to work with a List object. If GoldArray managed its data with a basic array, the client code directly above would have to work with an array object. The previous code, which uses C# .NET’s built-in iterator pattern, can enumerate GoldArray’s items regardless of changes to GoldArray’s internal array type. The client code could always remain “foreach (ItemContents item in MyGold)” and this is how we retain loosely-coupled classes within our iterator design pattern.
We could even create another class, which uses a completely different array structure, and use the same Iterator design pattern to traverse its items. Assume we have the following new class called SilverArray, which uses a basic array:
We have one piece of work, which is to create the SilverArrayIterator, just as we did for the GoldArray class. Remember, the iterator implementation knows the details of how to traverse SilverArray’s data:
Note, the difference between GoldArrayIterator2 and SilverArrayIterator2 is that GoldArrayIterator uses a List object and SilverArrayIterator relies on a basic array. However, since both implement the C# .NET IEnumerator interface, our client code doesn’t know the difference:
Notice in the client code example above, we use the same exact foreach loop to traverse the elements, even though MyGold and MySilver use a different internal array structure. The client never knows the difference. If changes were made inside GoldArray (and to its GoldArrayIterator), the client code would still remain the same.
The Iterator design pattern is a powerful pattern for traversing lists of objects while retaining loosely-coupled code between classes. The iterator pattern allows us to maximize code reuse and maintability through a common interface. As the iterator pattern is common in object-oriented programming languages, such as C# and Java, we can take advantage of default implementations in our language of choice. By using the iterator design pattern in our own C# ASP .NET web applications, we can enhance the lifetime of our code and the power of our applications.
This article was written by Kory Becker, software developer and architect, skilled in a range of technologies, including web application development, machine learning, artificial intelligence, and data science.