C# Iterators


This entry is part 7 of 7 in the series C# Generics

Enumerable classes and enumerators are used extensively in the .NET collection classes, so it’s important that you’re familiar with how they work. But now that you know how to create your own enumerable classes and enumerators, you might be pleased to learn that, starting with C# 2.0, the language provides a much simpler way of creating enumerators and enumerables. In fact, the compiler will create them for you.

The construct that produces them is called an iterator. You can use the enumerators and enumerables generated by iterators wherever you would use manually coded enumerators or enumerables.

using System;
using System.Collections.Generic;

class MyClass
{
    public int MyProperty { get; set; } // does nothing

    public IEnumerator<string> GetEnumerator()
    { return BlackAndWhite(); } // Returns the enumerator

    // IEnumerator<string> is code that returns an enumerator that returns strings
    public IEnumerator<string> BlackAndWhite() // Iterator
    {
        yield return "black";
        yield return "gray";
        yield return "white";
    }

    // IEnumerator<string> is code that returns an enumerator that returns strings
    public IEnumerator<string> BlackAndWhite_2() // Iterator version 2: same result
    {
        string[] theColors = { "black", "gray", "white" };
        for (int i = 0; i < theColors.Length; i++)
            yield return theColors[i];
    }
}
class Program
{
    static void Main()
    {
        MyClass mc = new MyClass();
        //Use the instance of MyClass.
        foreach (string shade in mc) Console.WriteLine(shade);
    }
}

Yield Return

What does yield return do? For example, in the first version, if the method returns on the first yield return statement, then the last two statements can never be reached. If it doesn’t return on the first statement but continues through to the end of the method, then what happens to the values? And in the second version, if the yield return statement in the body of the loop returns on the first iteration, then the loop will never get to any subsequent iterations. And besides all that, an enumerator doesn’t just return all the elements in one shot—it returns a new value with each access of the Current property. So, how does this give you an enumerator? Clearly this code is different from anything shown before.

Iterator Blocks

An iterator block is a code block with one or more yield statements. Any of the following three types of code blocks can be iterator blocks:

  • A method body
  • An accessor body
  • An operator body

Iterator blocks are treated differently than other blocks. Other blocks contain sequences of statements that are treated imperatively. That is, the first statement in the block is executed, followed by the subsequent statements, and eventually control leaves the block. An iterator block, on the other hand, is not a sequence of imperative commands to be executed at one time. Instead, it’s declarative; it describes the behavior of the enumerator class you want the compiler to build for you. The code in the iterator block describes how to enumerate the elements. As a side note, SQL is a declarative language. When a SQL SELECT statement is actually executed, it starts with the FROM clause, which is the second clause, not the first clause.

You can have an iterator block produce either an enumerator or an enumerable depending on the return type you specify.

Series Navigation<< C# Generic Enumeration Interfaces