The C# yield-foreach howto

Author: Kasper B. Graversen, 25/11/08
Keywords: CSharp, Yield, Foreach, IEnumerator, IEnumerable
Abstract: This article explains how to use the C# constructs yield-return, yield-break and foreach. We attack the problem from two different implementation angles using the types IEnumerator and IEnumerable.
subscribe to my RSS feed


Bookmark and Share


The C# yield-foreach howto

One of the cool features of the C# language, is the ability to quickly create an enumerable projection of what a class is an abstraction for. Providing an enumerator for users of the class is often a very desirable solution
  • The client is agnostic to the class' internal representation.
  • The client is loosely coupled to the class. It is only dependent on a very general interface when traversing the data.
  • The class may provide a read-only enumeration to the client for a more thread-safe or functional implementation approach.
  • Direct access using an index is not suitable to all data structures, in particular to data structures such as trees with no or slow random access.
  • An enumerator may enforce restrictions on access, such as ensuring that elements can not be skipped or that a previously visited element can not be accessed again.
While hand-coding an iterator is not a mission-impossible, it still is a difficult task for the beginner programmer. For the C# programmer this is no cause for concern as the language has native support for enumerators.

The C# language uses the keywords yield return and yield break to accomplish this. Basically, for each value to return using the yield return construct. And when there are no more values to return, use the yield break construct. At compile-time, the compiler will then generate a highly efficient state machine to perform the underlying business logic of the enumerator.

It is important to realize, that although syntactically almost identical to the Python language, the C# version is far less general. It is not a generator in the traditional sense, and hence is only applicable if the return type of the method is IEnumerable or IEnumerator!

Below is a class Program which exemplify a class that represents some collection of data. Users of this class can use getSomeStuff() to acquire an enumeration that can be iterated using foreach or LINQ. Central to this solution is the interface IEnumerable. More on this below.
using System;
using System.Collections.Generic;

namespace yield_foreach {
  class Program {
    static void Main (string[] args) {
      var p = new Program();
      foreach ( var s in p.getSomeStuff() ) {
        Console.WriteLine(s);
      }
    }

    public IEnumerable<int> getSomeStuff() {
      for ( int i = 0; i < 5; i++ ) {
        yield return i;
      }
      yield break;
    }
  }
}

The yield trap

While the example is straight forward, there is one trap lurking in the dark. Especially if you are a less experienced developer. The core "problem" is that the method getSomeStuff() can either have the return type IEnumerable or IEnumerator. If you use the IEnumerator type, however, the above program fails to compile with the message:
foreach statement cannot operate on variables of type 'System.Collections.Generic.IEnumerator<int>' because 'System.Collections.Generic.IEnumerator<int>' does not contain a public definition for 'GetEnumerator'

The problem, as stated in the compile-error, is that the IEnumerator type does not contain a method named GetEnumerator(). If we insist on using the return type, we can accomplish this with a few modifications as highlighted in bold. We simply change how we use the foreach and we rename the method to create the enumerator.
using System;
using System.Collections.Generic;

namespace yield_foreach {
  class Program {
    static void Main (string[] args) {
      var p = new Program();
      foreach ( var s in p ) {
        Console.WriteLine(s);
      }
    }

    public IEnumerator<int> GetEnumerator()
    {
      for ( int i = 0; i < 5; i++ ) {
        yield return i;
      }
      yield break;
    }
  }
}

Have fun with this powerful language construct.

-Kasper



Comments

If you have any comments to this article, please drop me a mail at firstclassthoughts at gmail dot com please indicate if I can't publish whole or parts of your comment on the site.


If you like this site consider subscribing to my RSS feed or how about subscribing by Email.


Help spread the word

Share this post on your favorite social bookmarking sites:
If you enjoyed this article, found it thought provoking, educative or otherwise good, please link to this page from your page or social bookmarking page. If you have any texts you think are worth publishing on First Class Thoughts, don't hesitate to send me a mail! Quality always welcome.


Bookmark and Share


The most recent contributions
28/07/09 Magic in mathematics II Fun with the number cyclic numbers, and specifically with 142857 as it is the smallest of such numbers.
13/07/09 My top 8 time-saving Firefox shortcuts This article presents my favorite top 8 time-saving shortcuts in Firefox 3.0 and Firefox 3.5. Get to know these and you'll be saving a lot of time. They have been ordered by "the element of most surprise"
20/05/09 Board Game Jungle speed / Arriba Review of the cool game "Jungle Speed" aka. "Arriba".
16/05/09 Danish Twin words "Twin words" are words that not only have multiple meanings, they must be composed next to each other in meaningful sentences. This article explores the concept of twin words.
Nothing of interest? Try browsing the entire article archive...