#562 – What an Iterator Looks Like Under the Covers

When you use an iterator to return an enumerable sequence of items, the C# compiler converts your iterator block (containing one or more yield return statements) into a new class that performs the actual enumeration.  The new class actually implements all four interfaces that an iterator can return–IEnumerableIEnumerable<T>IEnumerator and IEnumerator<T>.

Below is a simple iterator block that returns a sequence of two elements.

        private static IEnumerable<int> MyIterator()
        {
            yield return 100;
            yield return 200;
        }

If you use a disassembler to look at the IL that the compiler generates for this code, you’ll see a new class that implements the four interfaces.

You’ll notice that the class implements GetEnumerator, which just returns the current instance of the class.  (Code shown is from Reflector).

The MoveNext method returns the next element in the sequence.

Advertisement

#561 – Using a yield break Statement

When implementing an iteratorthe yield return statement returns the next element in the sequence being returned.  If you are using a loop within the iterator block, you can use the yield break statement to break out of the loop, indicating that no more elements are to be returned.

In the example below, an iterator is used to return a portion of the Fibonacci sequence.  yield break is used to break out of the loop generating the sequence.

        static void Main()
        {
            // Return Fibonacci numbers below 2,000
            foreach (int i in Fibonacci(2000))
                Console.WriteLine(i);

            int gi = 12;
        }

        private static IEnumerable<int> Fibonacci(int maxValue)
        {
            yield return 0;
            yield return 1;

            int last = 0;
            int current = 1;

            while (true)
            {
                int next = last + current;
                if (next > maxValue)
                    yield break;
                else
                {
                    yield return next;
                    last = current;
                    current = next;
                }
            }
        }

#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);

#559 – An Iterator Can Return an Enumerable Type or an Enumerator

An iterator is a block of code that contains one or more yield statements.  It can return one of the following interfaces:

  • IEnumerable  (in System.Collections)
  • IEnumerable<T>  (in System.Collections.Generic)
  • IEnumerator  (in System.Collections)
  • IEnumerator<T>  (in System.Collections.Generic)

You’d return an enumerable type when you want provide client code with a collection of objects and you don’t want to write your own custom collection class.

You’d typically return an enumerator when you’re implementing the GetEnumerator method in a custom collection class.

 

#558 – Using an Iterator Within a for Loop

An iterator block could contain a series of yield statements to return individual elements of a sequence.

        private static IEnumerable<Dog> ListOfDogs()
        {
            yield return new Dog("Jack", 17);
            yield return new Dog("Kirby", 14);
            yield return new Dog("Lassie", 72);
        }

More commonly, however, an iterator block will contain a loop that generates a sequence, with a yield statement that returns a new element of the sequence each time through the loop.

The example below returns a portion of the Fibonacci sequence, with each element in the sequence after the first two returned by a yield statement within the loop.

        static void Main()
        {
            foreach (int i in Fibonacci(20))
                Console.WriteLine(i);
        }

        private static IEnumerable<int> Fibonacci(int numInSeq)
        {
            yield return 0;
            yield return 1;

            int last = 0;
            int current = 1;

            for (int i = 0; i < (numInSeq - 2); i++)
            {
                int next = last + current;
                yield return next;
                last = current;
                current = next;
            }
        }

#557 – Using an Iterator to Return the Elements of an Enumerable Type

An iterator is a block of code that produces an enumerator.  (An enumerator is an object that knows how to move through a sequence of elements, typically implemented in an enumerable type that represents a collection of elements).

You can think of the iterator as the code that generates an enumerator, whereas a foreach loop is the code that makes use of the enumerator.

An iterator block is a block of code that includes one or more yield statements, each of which returns the next item in the sequence.  The iterator block can return either an enumerable type or an enumerator.  In the example below, the enumerable type IEnumerable<Dog> is returned.

        static void Main()
        {
            foreach (Dog d in ListOfDogs())
                Console.WriteLine(d.Name);
        }

        private static IEnumerable<Dog> ListOfDogs()
        {
            yield return new Dog("Jack", 17);
            yield return new Dog("Kirby", 14);
            yield return new Dog("Lassie", 72);
            yield return new Dog("Rin Tin Tin", 94);
        }

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

#554 – Rules for Matching an Anonymous Method to a Delegate Type

When you declare an anonymous method and assign it to a delegate type or pass it as a delegate-typed parameter, the anonymous method is converted to an instance of the appropriate delegate.  There are several rules that you must follow so that this conversion can happen properly.

  • You can only omit the formal parameter list in the anonymous method declaration if
    • The delegate type’s parameter list has no ref or out parameters
    • The body of the anonymous method does not need to read or write any of the parameters
  • If you include the formal parameter list in the anonymous method declaration, you must include all of the parameters and the type must match for each parameter
  • Any object returned as the return value of the anonymous method must be able to be implicitly converted to the return type of the delegate

#553 – Anonymous Methods as Static or Instance Methods

An anonymous method that is declared within an instance method is considered itself to be an instance method of the class in which it was defined.  This means that it can make use of instance data within the class.

In the example below, an anonymous method is declared within the DogInteractions.BarkAtNeighbor method.  Since this is an instance method, the body of the anonymous method can make use of the numBarks instance variable.

    public class DogInteractions
    {
        private int numBarks = 0;

        public void BarkAtNeighbor(Dog d, string neighbor)
        {
            // Delegate passed into the Bark method,
            //   which will then invoke it.
            d.Bark(
                delegate(string barkSound)
                {
                    numBarks++;
                    Console.WriteLine("Bark #{0} - Hey {1}: {2}", numBarks, neighbor, barkSound);
                });
        }
    }

Similarly, if the anonymous method was declared within a static method, it would be considered itself to be static and could not make use of instance data within the class.