Hemme's Blog

Stories from behind the keyboard

  • RSS
  • Twitter
  • Linkedin

Some days ago I was demonstrating the use of the yield C# keyword. As MSDN states

The yield keyword signals to the compiler that the method in which it appears is an iterator block. The compiler generates a class to implement the behavior that is expressed in the iterator block

One of the most non-intuitive effects of the yield operator is that when you are visiting the collection, what’s declared inside the block persists in memory throughout the cycle, and the body of the yield block is invoked multiple times, corresponding to each step of the visiting cycle.

For instance, let’s write an iterator block such the following.

IEnumerable<int> Numbers(int min, int max)
{
  int n = min;
  while(n<=max) {
    //Dump invocation time here  
    yield return n++;
  }
}

The comment “Dump invocation time here” invites you to write some code to track the time each yield instruction is being invoked. In this sample, I used the LINQPad, which you can download and test here. By the way, LINQPad is a great piece of software allowing you to execute instant .Net code without creating stuff like Projects or Solutions. Moreover it’s a great tool to test and improve your LINQ skills.

The simplest way to dump something in LINQPad is invoking the Dump() extension method. My iterator code becomes:

IEnumerable<int> Numbers(int min, int max)
{
  int n = min;
  while(n<=max) {
    var stats = new {
      Time = DateTime.Now.ToLongTimeString(),
      Value = n
    };
    stats.Dump(); //this one dumps the stats on the LINQPad output window

    yield return n++;
  }
}

In order to test the Numbers() method, I wrote the following code:

void Main()
{
  DelayVisit(Numbers(2,10));
  Numbers(30,33).Dump();
}
void DelayVisit(IEnumerable<int> e)
{
  foreach(var i in e) // VISITING LOOP
  {
    System.Threading.Thread.Sleep(1000);
  }
}

Main() is the entry point; it invokes DelayVisit() which in turn visits the collection returned (built) by the Numbers() function, waiting 1 second between each loop step. The second line of the Main() method cycles through the elements returned by the Numbers() method (in effect, Dump() “recognizes” the invoker is an IEnumerable, so it dumps each element of the collection).

The output of this test is shown in the following picture. As you can see, the inner block of the while() loop defined in the Numbers() function is invoked at intervals of 1 second. This is an evidence that the yield loop is “surviving” among each cycle of the visiting loop.

yield

No comments: