 .NET 1.1+Iterator Design PatternThe iterator pattern is a design pattern that provides a means for the elements of an aggregate object to be accessed sequentially without knowledge of its structure. This allows traversing of lists, trees and other structures in a standard manner.
What is the Iterator Pattern?
The iterator pattern is a Gang of Four design pattern. This is a behavioural pattern as it defines a manner for controlling communication between classes or entities. The iterator pattern is used to provide a standard interface for traversing a collection of items in an aggregate object without the need to understand the underlying structure of that object. The interface provided is generally simplistic, providing methods to move to the next item, return to the beginning of the list, retrieve the current item and determine if the end of the list has been reached.
A variation upon the iterator design pattern is used extensively within C# and the .NET framework. Iterators are included in the standard array and collection classes and can easily be added to your own classes by implementing the IEnumerable and IEnumerator interfaces. C# version 2.0 also includes the yield keyword to simplify the process of creating iterators.
In most cases using the options provided by the C# language and the .NET framework is ideal when you wish to implement the iterator design pattern. In this article we will firstly implement a classic version of the pattern. We will then modify this to use the IEnumerator and IEnumerable interfaces and review the differences.
Implementing the Classic Iterator Pattern

The UML class diagram above describes a classic implementation of the iterator design pattern. The items in the diagram are described below:
- Client. Objects of this type are the consumers of the iterator design pattern. They request an iterator from an aggregate object when they wish to loop through the items that it holds. The methods of the iterator are then used to retrieve items from the aggregate in an appropriate sequence.
- AggregateBase. This abstract class is the base class for aggregate objects. It includes a method that generates an iterator, which can be used to obtain references to the objects that subclasses contain. This class is often implemented as an interface.
- ConcreteAggregate. The concrete aggregate classes provide the real functionality for aggregate objects that contain collections of items that can be traversed using an iterator.
- IteratorBase. This abstract class is the base class for iterators. It defines a standard interface that includes methods to allow the elements of the aggregate that generated it to be looped through in sequence. This class is often implemented as a simple interface.
- ConcreteIterator. Concrete iterators implement the interface defined by the IteratorBase class. They provide functionality specific to the ConcreteAggregate class used to generate them, hiding the implementation of the aggregate from the client.
The following shows the basic code of the iterator design pattern implemented using C#. In this case the aggregate's underlying objects are held in an ArrayList. This class is found in the System.Collections namespace so include the following using directive:
using System.Collections;
An indexer is used to allow read-only access to the items from the ArrayList from the iterator and a Count property allows the iterator to determine the size of the collection. This simplified code is useful for demonstrating the pattern but is unlikely to be required in a real solution as the ArrayList already provides iterator functionality through the IEnumerable interface.
public class Client
{
public void UseIterator()
{
ConcreteAggregate aggr = new ConcreteAggregate();
aggr.Add("One");
aggr.Add("Two");
aggr.Add("Three");
aggr.Add("Four");
aggr.Add("Five");
IteratorBase iterator = aggr.CreateIterator();
string item = (string)iterator.First();
while (!iterator.IsDone())
{
Console.WriteLine(item);
item = (string)iterator.Next();
}
}
}
public abstract class AggregateBase
{
public abstract IteratorBase CreateIterator();
}
public class ConcreteAggregate : AggregateBase
{
private ArrayList _items = new ArrayList();
public override IteratorBase CreateIterator()
{
return new ConcreteIterator(this);
}
public object this[int index]
{
get { return _items[index]; }
}
public int Count
{
get { return _items.Count; }
}
public void Add(object o)
{
_items.Add(o);
}
}
public abstract class IteratorBase
{
public abstract object First();
public abstract object Next();
public abstract object CurrentItem();
public abstract bool IsDone();
}
public class ConcreteIterator : IteratorBase
{
private ConcreteAggregate _aggregate;
int _position;
public ConcreteIterator(ConcreteAggregate aggregate)
{
_aggregate = aggregate;
_position = 0;
}
public override object First()
{
_position = 0;
return CurrentItem();
}
public override object Next()
{
_position++;
return CurrentItem();
}
public override object CurrentItem()
{
if (_position < _aggregate.Count)
{
return _aggregate[_position];
}
else
{
return null;
}
}
public override bool IsDone()
{
return _position >= _aggregate.Count;
}
}
Implementing the Iterator Pattern with .NET Framework Interfaces
In this section of the article we will implement a second variation of the sample above. This time we will use the IEnumerable and IEnumerator interfaces provided by the .NET framework. When you are using the .NET framework version 2.0 and later, there are generic versions of these interfaces available. So that this article supports version 1.1 of the framework we will use the non-generic versions.
The first modification to the above code is the removal of the AggregateBase class. This class is no longer required as it is replaced by the IEnumerable interface. We then need to modify the ConcreteAggregate so that it implements IEnumerable. Note that the CreateIterator method is replaced with the GetEnumerator method, which returns an IEnumerator instance.
public class ConcreteAggregate : IEnumerable
{
private ArrayList _items = new ArrayList();
public IEnumerator GetEnumerator()
{
return new ConcreteIterator(this);
}
public object this[int index]
{
get { return _items[index]; }
}
public int Count
{
get { return _items.Count; }
}
public void Add(object o)
{
_items.Add(o);
}
}
The next change is to remove the IteratorBase class. The methods of this base class are replaced with methods and properties from the IEnumerator interface. The ConcreteIterator must be updated to implement this interface, which is slightly different to that already in the code.
The Next method is renamed "MoveNext" and returns true if another item existed and false if the end of the sequence was passed. This allows the IsDone method to be removed altogether. The First method is renamed to "Reset" and no longer returns any value. Rather than moving the current position to the first item in the sequence, the position is actually set to be before the sequence start. This is so that the first call to MoveNext selects the first item. The CurrentItem method is replaced with the "Current" property. Finally, rather than returning null when the sequence is exhausted, an InvalidOperationException is thrown as the Current property should not be read at this time.
The updated code for the ConcreteIterator is as follows:
public class ConcreteIterator : IEnumerator
{
private ConcreteAggregate _aggregate;
int _position;
public ConcreteIterator(ConcreteAggregate aggregate)
{
_aggregate = aggregate;
_position = 0;
}
public void Reset()
{
_position = -1;
}
public bool MoveNext()
{
_position++;
return _position < _aggregate.Count;
}
public object Current
{
get
{
if (_position < _aggregate.Count)
{
return _aggregate[_position];
}
else
{
throw new InvalidOperationException();
}
}
}
}
Testing the Iterator
To test the second variation of the iterator pattern we can modify the code that was previously in the Client class and move it to the Main method of a console application. Try executing the following code to see the results:
ConcreteAggregate aggr = new ConcreteAggregate();
aggr.Add("One");
aggr.Add("Two");
aggr.Add("Three");
aggr.Add("Four");
aggr.Add("Five");
IEnumerator iterator = aggr.GetEnumerator();
iterator.Reset();
while (iterator.MoveNext())
{
string item = (string)iterator.Current;
Console.WriteLine(item);
}
|