#563 – Enumerable Types Can Generate Multiple Enumerators

Each time that the GetEnumerator method in an enumerable type is called, an independent enumerator is generated.  Each enumerator keeps track of its own position in the parent sequence.

In the example below, nested foreach statements each iterate over the same collection.  The outer foreach statement results in one enumerator and the inner foreach statement results in the generation of a new enumerator for each pass of the outer foreach.

In this case, the ability to generate multiple independent enumerators is necessary, so that we can have overlapping foreach statements.

            int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

            foreach (int i in nums)
                foreach (int j in nums)
                    Console.WriteLine("{0} x {1} = {2}", i, j, i * j);

Advertisement

#560 – Returning an Enumerator from an Iterator

You can return an enumerator from an iterator block.  This is something that you’d typically do when implementing your own collection class.

Below, we implement the DogsAndCows collection class, which manages a group of Dog and Cow objects.  It implements the IEnumerable interface, which means that it implements the GetEnumerator method, allowing iterating through the entire collection.

GetEnumerator uses an iterator block to return the enumerator.

    // Collection of dogs and cows
    public class DogsAndCows : IEnumerable
    {
        private List<Dog> theDogs = new List<Dog>();
        private List<Cow> theCows = new List<Cow>();

        public void AddDog(Dog d)
        {
            theDogs.Add(d);
        }

        public void AddCow(Cow c)
        {
            theCows.Add(c);
        }

        // Return first the dogs and then the cows
        public IEnumerator GetEnumerator()
        {
            foreach (Dog d in theDogs)
                yield return d;

            foreach (Cow c in theCows)
                yield return c;
        }
    }

We can iterate through this collection with foreach.

            foreach (IAnimal animal in oddPack)
                Console.WriteLine(animal.Name);

#556 – Using an Enumerator Explicitly

An enumerator is an object that knows how to move through a sequence of elements.  In C#, the foreach statement provides this same functionality wrapping an enumerator.

You can use an enumerator to iterate through a collection, rather than a foreach statement.  This helps in understanding how enumerators work.

The code fragment below shows iterating through a collection using the foreach statement and then doing the same thing using an enumerator.  (dogs is of type List<Dog>).

            // Using foreach
            foreach (Dog d in dogs)
                Console.WriteLine(d.Name);

            // Using enumerator directly
            List<Dog>.Enumerator dogEnum = dogs.GetEnumerator();
            while (dogEnum.MoveNext())
            {
                Dog d = (Dog)dogEnum.Current;
                Console.WriteLine(d.Name);
            }

The enumerator is an object of type List<T>.Enumerator.  We call a method on the List<T> object to get an instance of its enumerator and then navigate through the list using the MoveNext method.  The Current property returns the object that the enumerator is currently pointing to.

#555 – Enumerable Objects and Enumerators

An enumerable object is a type that represents a sequence of elements.  The type implements either the IEnumerable or IEnumerable<T> interface, which means that it implements a GetEnumerator method, which is called to get the enumerator for the enumerable class.

An enumerator is an object that knows how to move through a sequence of elements.  It implements either the IEnumerator or IEnumerator<T> interface, which means that it exposes a Current property that points to the current element in the sequence and a MoveNext method that moves to the next element.

You can think of an enumerator as a sort of pointer into a sequence of elements.

Enumerators, used on enumerable objects, are the internal mechanism that allows using a foreach statement to iterate through a collection of items.