A closer look at yield

The yield keyword in C# is pretty powerful and expressive, but it doesn’t seem to be very widely known about. In this post we’ll take a quick look at what yield does and then I’ll post a follow-up that looks at what the compiler generates for you. Let’s start by looking at a simple (and contrived) example:

 private static readonly string[] StringValues = new string[] { "The", "quick", "brown", "fox", 
                             "jumped", "over", "the", "lazy", "dog" };
 static IEnumerable<string> TestIterator()
{
    foreach(string value in StringValues)
    {
         yield return value;
    }
}

The return type for the TestIterator method is IEnumerable<string>, but you can see that we don’t have a return statement in the implementation. Instead, we’re using the yield return statement to return each item that we want the caller to operate on. The compiler automatically generates a class that implements IEnumerable<string> for us. We can call this function using the following code:

 foreach(string value in TestIterator())
{
    Console.WriteLine("In foreach:{0}", value);
}

This code will iterate over the IEnumerable<string> instance that is returned from the TestIterator method. In this example we’ve simply iterated over an existing collection, so we’re not providing much functionality! A more interesting example would be for a binary tree such as:

 class BinaryTree<T>
{
    public T Item { get; set; }
    public BinaryTree<T> Left { get; set; }
    public BinaryTree<T> Right { get; set; }
}

In this case, the yield keyword makes it a breeze to add IEnumerable support. First, we add IEnumerable<T> to the implemented interfaces and then we just need the code below to provide the implementation:

 public IEnumerator<T> GetEnumerator()
{
    yield return Item;
    if (Left != null)
    {
        foreach (T t in Left)
        {
            yield return t;
        }
    }
    if (Right != null)
    {
        foreach (T t in Right)
        {
            yield return t;
        }
    }
}

This code returns the item for the current node and then recurses into the items from the left and right hand of the tree – how easy is that? This is one of the big advantages of the yield keyword: it allows you to write readable and concise code to produce an iterator.

Stay tuned for part 2, when we’ll take a look at the code that the compiler generates for us...