BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

Design Patterns
.NET 1.1+

Iterator Design Pattern

The 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.

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);
}
26 July 2009