Data Structures and Algorithms with Object-Oriented Design Patterns in C#
next up previous contents index

Enumerable Collections and Enumerators

 

In this section we introduce an abstraction called an enumerator. An enumerator provides the means to access one-by-one all the objects in a container. Enumerators are an alternative to using the visitors described in Section gif.

The Container interface given in Program gif extends the standard C# IEnumerable interface . The following code fragment defines the IEnumerable interface.

namespace System.Collections
{
    public interface IEnumerable
    {
	public IEnumerator GetEnumerator();
    }
}
Simply put, a class that is enumerable provides a method that returns an enumerator.

The idea is that for every concrete container class we will also implement a related class that implements the standard C# IEnumerator interface . The following code fragment defines the IEnumerator interface.

namespace System.Collections
{
    public interface IEnumerator
    {
	public bool MoveNext();
	public object Current { get; }
	public void Reset();
    }
}
The interface comprises two methods, MoveNext and Reset, and a property, Current.

In order to understand the desired semantics, it is best to consider first an example which illustrates the use of an enumerator. Consider a concrete container class, say SomeContainer, that implements the Container interface. The following code fragment illustrates the use of the enumerator to access one-by-one the objects contained in the container:

Container c = new SomeContainer();
// ...
IEnumerator e = c.GetEnumerator();
while (e.MoveNext())
{
    object obj = e.Current;
    Console.WriteLine(obj);
}
e.Reset();

In order to have the desired effect, the members of the IEnumerator interface must have the following behaviors:

MoveNext
The MoveNext method is called in the loop-termination test part of the while statement. The MoveNext conceptually moves the enumerator to the next object to be visited. The MoveNext method returns true when there are still more objects in the container to be visited and it returns false when all of the contained objects have been visited.
Current
The Current property provides a get accessor that returns the next object in the container to be visited. If there is no current object to be visited, this accessor throws a InvalidOperationException exception.
Reset
The Reset method resets the enumerator so that all the objects in the container can be visited again.

Given these semantics for the enumerator methods, the program fragment shown above systematically visits all of the objects in the container and prints each one on its own line of the console.

One of the advantages of using an enumerator object which is separate from the container is that it is possible then to have more than one enumerator associated with a given container. This provides greater flexibility than possible using a visitor, since only one visitor can be accepted by a container at any given time. For example, consider the following code fragment:

Container c = new SomeContainer();
// ...
IEnumerator e1 = c.GetEnumerator();
while (e1.MoveNext())
{
    object obj1 = e1.Current;
    IEnumerator e2 = c.GetEnumerator();
    while (e2.MoveNext())
    {
	object obj2 = e2.Current;
	if (obj1.Equals(obj2))
	    Console.WriteLine(obj1 + "=" + obj2);
    }
}
This code compares all pairs of objects in the container c and prints out those which are equal.

A certain amount of care is required when defining and using enumerators. In order to simplify the implementation of enumerators, we shall assume that while an enumerator is in use, the associated container will not be modified.




next up previous contents index

Bruno Copyright © 2001 by Bruno R. Preiss, P.Eng. All rights reserved.