Visual Studio 2010!

Read now >

View Now
DevSource RSS FEEDS
XML Want an easy way to keep up with breaking tech news? And the Get DevSource headlines delivered to your desktop with RSS.
ADVERTISEMENT
ADVERTISEMENT

 

DevSource.com: Your Source for Visual Studio on Facebook
ADVERTISEMENT
C#: Dynamic Iterators with Yield Return
By Paul Kimmel

Rate This Article: Add This Article To:

C#: Dynamic Iterators with Yield Return - ' How it works '
( Page 2 of 3 )

Listing 2 (shown at the end of this article on Page 3) contains the entire console application, including the ScrabbleWord and Dictionary classes. The Dictionary.Get method demonstrates how to use the yield return construct.

At first glance the return yield in Dictionary.Get looks weird. What it does is basically returns an instance of IEnumerable<T>; in this case T is a ScrabbleWord. Literally yield return eliminates the need to create an instance of Dictionary<T>, populate that with only the ScrabbleWord objects we want, and return that Dictionary instance. In short, we don't have to copy a bunch of objects, again.

How it works is pretty cool too.

Understanding How Return Yield Works

When the compiler sees yield return (see Listing 3) it emits a nested private class. This nested private class will be named something like <Get>d__0. More importantly, the nested class implements IEnumerable, IEnumerator, and IDisposable. In short the dynamically generated class is a kind of state machine that uses an enumerator (an implementation of the Iterator behavior pattern) to keep track of where your code is in the iteration process. (Listing 4—ala Lutz Roeder's Reflector—shows us the class generated by using yield return.)

Listing 3: An excerpt showing the yield return statement.

public IEnumerable<ScrabbleWord> Get(string regex)
{
  foreach(ScrabbleWord o in this)
    if(Regex.IsMatch(o.Word, regex))
      yield return o;
}

Listing 4: Yield return generates a dynamic, private, nested iterator.

[CompilerGenerated]
private sealed class <Get>d__0 : 
IEnumerable<Program.ScrabbleWord>, IEnumerable,
IEnumerator<Program.ScrabbleWord>, IEnumerator, 
IDisposable
{
    // Fields
    private int <>1__state;
    private Program.ScrabbleWord <>2__current;
    public string <>3__regex;
    public Program.Dictionary <>4__this;
    public List<Program.ScrabbleWord>.Enumerator 
      <>7__wrap2;
    public Program.ScrabbleWord <o>5__1;
    public string regex;

    // Methods
    [DebuggerHidden]
    public <Get>d__0(int <>1__state);
    private bool MoveNext();
    [DebuggerHidden]
    IEnumerator<Program.ScrabbleWord>
       IEnumerable<Program.ScrabbleWord>.
       GetEnumerator();
    [DebuggerHidden]
    IEnumerator IEnumerable.GetEnumerator();
    [DebuggerHidden]
    void IEnumerator.Reset();
    void IDisposable.Dispose();

    // Properties
    Program.ScrabbleWord IEnumerator<Program.ScrabbleWord>
       .Current { [DebuggerHidden] get; }
    object IEnumerator.Current { [DebuggerHidden] get; }
}

When you debug yield return, it looks like with the foreach iteration your code is jumping into the for loop containing yield return and bouncing back out without unwinding the stack (or running finally blocks) until the for loop is completely finished. In essence it looks like the method—Get in our example—is maintaining state intra-function. Under the hood though I wrote

foreach(ScrabbleWord word in d.Get(@"^A\w+E$"))
  Console.WriteLine(word);

but the framework emitted an iterator class and consequently emitted this code (again ala Lutz Roeder).

using (IEnumerator<ScrabbleWord> CS$5$0000 = 
d.Get(@"^A\w+E$").GetEnumerator())
{
  while (CS$5$0000.MoveNext())
  {
    Console.WriteLine(CS$5$0000.get_Current());
  }
}

Summary

Real frameworks do an astounding number of big and small jobs on our behalf. A simple construct like yield return is actually going to generate an instance of the Iterator behavior pattern and turn what looks like an ordinary function into a state machine that eliminates the need to create objects and populate them when all we really want is access to the subset of data.

Real frameworks do a lot of drudge work for we beleaguered programmers, and clearly there is a lot going on under the .NET hood.



 
 
>>> More Microsoft Languages Articles          >>> More By Paul Kimmel