Complex syntactical sugar using yield in C#






Yield is one of the available contextual keywords in C#, meaning it has a purpose in the code but it’s not a reserved word. Microsoft defines yield as being:

Used in an iterator block to return a value to the enumerator object or to signal the end of iteration

The keyword was added in .NET 2.0 and can basically be described as complex syntactical sugar. As described in the definition above yield is used to add a value to the enumerator object. That could look like something like this:

public static IEnumerable<Car> GetRedCars()
{
  foreach(Car car in ourCars)
  {
    if (car.Color == "Red")
    {
      yield return car;
    }                
  }
}

At a first glance it might look like yield only adds a value to the returned IEnumerable object in a more neat way. But there is more to it under the hood. The IEnumerable implemented object created by yield “remembers” where in the array it is and only processes the next value to be returned.

What’s the use of that? Well, if you know that you’ll use the whole list of returned objects then the benefits are syntactical only and you might find the overhead added to be of disadvantage. But lets say you have a list of 1000 objects being returned but you’re only interested in the first 3 ones, then yield will never bother in processing the final 997 ones. Another useful example is when you’re processing infinite numbers, like the Fibonacci series (illustrated in the example below). Trying to return all the numbers will return in buffer overflow but by using yield the object only processes the next upcoming number. The key to how many numbers that will be processed is in how far you iterate through the returned IEnumerable object. For each lap the next yield is processed.

In this example we implement the infinite Fibonacci number serie starting with 1 1 2 3 5 8 13 21 and so on.

public static IEnumerable<Int64> GetFibonacci()
{
  Int64 prev = 1;
  yield return prev;      // The first Fibonacci is 1
  
  Int64 current = 1;
  yield return current;   // The second Fibonacci is 1

  // The rest of the Fibonacci numbers are calculated
  while (true)
  {
    Int64 temp = prev;
    prev = current;     // Moving current to previous value
    current = temp + current;   // Calculate new current
    yield return current;   // Yield the new current
  }
}

When using the GetFibonacci function you have to remember that it’s infinite and implement your own code to break when needed. Here we use a simple console application to call the function.

static void Main(string[] args)
{
  Int64 current = 0;
  foreach(Int64 num in GetFibonacci())
  {
    Console.WriteLine(num.ToString());
    if (++current == 10)   // 92 is the maximum for Int64
      break;
  }
  Console.ReadKey();
}

This works great as shown in this screen dump.

Fibonacci2

But to better understand in what order things are executed we can put the code inside a try-finally block and add some extra Console.WriteLine calls.

public static IEnumerable GetFibonacci()
{
  Int32 lapNum = 0;
  try
  {
    Int64 prev = 1; 
    Int64 current = 1;

    lapNum++; 
    yield return prev;      // The first Fibonacci is 1

    lapNum++;
    yield return current;   // The second Fibonacci is 1

    // The rest of the Fibonacci numbers are calculated
    while (true)
    {
      Int64 temp = prev;
      prev = current;     // Moving current to previous value
      current = temp + current;   // Calculate new current
                    
      lapNum++; 
      Console.WriteLine(String.Format(" Processing lap {0} with the value of {1}", lapNum.ToString(), current.ToString()));
      yield return current;   // Yield the new current
    }
  }
  finally 
  {
    Console.WriteLine(String.Format(" We stop here after {0} laps...", lapNum.ToString()));
  }
}

The result will then look like this.

Fibonacci result






Note two things in this final example:

  1. The Console.WriteLine call inside the loop (line 23) is only called when the next number from the IEnumerable object is accessed. If you were to create a finite list of Fibonacci numbers, without yield, and then return them you would first see these calls as the numbers are created, and then later on the “Showing Fibonacci number…” lines.
  2. The finally block isn’t executed until the loop inside the try-block is completed, or, as in our case, we dispose of the IEnumerable object by breaking the calling loop in the console application.

If you’re used to programming in LINQ you might recognize some of the features because yield is something that is heavily relied upon within LINQ.