#1,197 – Iterator Can Generate Enumerable of Infinite Length

An iterator can be written to generate an enumerable sequence of infinite length.  The iterator will execute for as long as client code continues to request the next element in the sequence.

Below is a simple iterator that generates prime numbers.  Note that we can still use the iterator, although its implementation includes an infinite loop.  The loop will execute only as many times as needed.

        static void Main(string[] args)
        {
            Console.Write("Enter limit:");
            int limit = int.Parse(Console.ReadLine());

            foreach (int p in AllPrimes())
            {
                if (p > limit)
                    break;
                Console.WriteLine(p);
            }

            Console.ReadLine();
        }

        private static IEnumerable<int> AllPrimes()
        {
            List<int> primesSoFar = new List<int>();
            primesSoFar.Add(2);
            yield return 2;
            primesSoFar.Add(3);
            yield return 3;

            int testPrime = 5;
            while (true)
            {
                bool isPrime = true;
                foreach (int n in primesSoFar)
                {
                    if (testPrime % n == 0)
                    {
                        isPrime = false;
                        break;
                    }
                }

                if (isPrime)
                {
                    primesSoFar.Add(testPrime);
                    yield return testPrime;
                }

                testPrime += 2;
            }
        }

1197-001

Advertisement

#1,195 – Iterator Produces Only as Many Elements as Are Needed

When implementing an iterator, you write code that generates a sequence of elements using the yield return statement to return each consecutive element.

The full body of the iterator code may not execute.  Only the code required to return the elements iterated upon is executed.  In the example below, we iterate over a sequence produced by an iterator, using foreach.

Executing this code, only a portion of the AllMyDogs() method is executed, since we exit early out of  the foreach loop.

            static void Main(string[] args)
            {
                foreach (Dog d in AllMyDogs())
                {
                    Console.WriteLine(d);
                    if (d.Breed == Breed.JackRussell)
                        break;
                }

                Console.ReadLine();
            }

            private static IEnumerable<Dog> AllMyDogs()
            {
                Console.WriteLine("* returning Kirby");
                yield return new Dog("Kirby", Breed.BorderCollie, 14);
                Console.WriteLine("* returning Jack");
                yield return new Dog("Jack", Breed.JackRussell, 15);
                Console.WriteLine("* returning Ruby");
                yield return new Dog("Ruby", Breed.Mutt, 4);
                Console.WriteLine("* returning Lassie");
                yield return new Dog("Lassie", Breed.Collie, 12);
                Console.WriteLine("* returning Foofoo");
                yield return new Dog("Foofoo", Breed.Sheltie, 8);
            }

1195-001

#1,118 – foreach Works with Iterator Returned by yield

You can use the yield keyword in a method to implement an iterator.  The method in which it is defined returns an IEnumerable or IEnumerator.  The resulting iterator can be consumed using the foreach statement.

Below is an example of a method that returns an IEnumerable<T> which can then be iterated upon using a foreach statement.

        static IEnumerable<Dog> DogsWithKids(IEnumerable<Dog> dogList)
        {
            foreach (Dog d in dogList)
            {
                yield return d;
                yield return new Dog(d.Name + " Jr.",
                                     Math.Max(d.Age - 3, 1));
            }
        }

        static void Main(string[] args)
        {
            List<Dog> myDogs = new List<Dog>
                {
                    new Dog {Name = "Kirby", Age = 15},
                    new Dog {Name = "Ruby", Age = 2}
                };

            // Iterate through dogs w/offsprings
            foreach (Dog d in DogsWithKids(myDogs))
                Console.WriteLine(d);

            Console.ReadLine();
        }

The result is a sequence with twice as many elements as the original List<T>.

1118-001

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

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

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