Back To Basics

The yield return and yield break keywords are shortcuts introduced in C# 3.0. They are designed to assist you in a method that returns IEnumerable .
Often such methods consist of the following steps

  1. Create an empty IEnumerable set.
  2. Loop through some data (via a while, foreach or other construct)
  3. Add to the set within your loop.
  4. Exit the loop when some limit is reached.
  5. Return the set.

A sample of such code is shown below.

public IEnumerable GetOddNumbers_Old(Int32 maxNumber)
{
    List oddSet = new List();
    int num = 0;
    while (true)
    {
        System.Threading.Thread.Sleep(1000);
        num++;
        if (num % 2 == 1)
        {
            oddSet.Add (num);
        }
        if (num >= maxNumber)
        {
            break;
        }
    }

    return oddSet;
}

The new yield keywords allow you to shorten your code to only the following steps

  1. Loop through some data
  2. yield return within the loop
  3. yield break if a limit is reached before the loop is complete

That’s it. There is no need to explicitly create or return your IEnumerable set.

The yield return statement appends a value to the IEnumerable set. The yield break statement exits any looping construct to prevent more items being added to the return set. If you omit yield break, the loop exits as it normally would.

The difference between using the yield return and simply returning a set is subtle. Returning a set at the end of the method returns the entire set at once. The client must wait until the method is complete before it can use any values in the return set. Each time the yield return statement executes, the client has access to another element in the set. Essentially the set trickles back a little at a time. This can provide a gain in performance or perceived performance if you have a large set to return.

Here is a sample that returns the same results as above, using yield return and yield break.

public IEnumerable GetOddNumbers_Yield(Int32 maxNumber)
{
    int num = 0;
    while (true)
    {
System.Threading.Thread.Sleep(1000); num++; if (num % 2 == 1) { yield return num; } if (num >= maxNumber) { yield break; } } }

If we call the two methods above, we will get the same results but they will return in slightly different ways. I added the System.Threading.Thread.Sleep(1000); line to help demonstrate this. That line simply pauses execution for 1 second each time the loop executes.

Assume the above two methods are in a class named MyMethods.  The following code will call each.

Console.WriteLine("Get Odd Numbers using old way:");
MyMethods mm = new MyMethods();
IEnumerable oddNumbers2 = mm.GetOddNumbers_Old(11);
foreach (Int32 n in oddNumbers2)
{
    Console.WriteLine(n);
}

Console.WriteLine();


Console.WriteLine("Get Odd Numbers using new YIELD keyword:");
IEnumerable oddNumbers1 = mm.GetOddNumbers_Old(11);
foreach (Int32 n in oddNumbers1)
{
    Console.WriteLine(n);
}

Console.ReadLine();

The above code produces the following output

Get Odd Numbers using old way:
1
3
5
7
9
11

Get Odd Numbers using new YIELD keyword:
1
3
5
7
9
11

Notice that both methods return the same data. However, if you watch the code as it runs, you will notice the first method waits 12 seconds, then immediately outputs all odd numbers; while the second method outputs a number every 2 seconds. Each method returns the same values and each takes the same amount of time, but the second method uses yield return, so the client is able to output a bit of data each time a yield return executes.

There are a few restrictions to using the yield keywords

  • These keywords cannot appear in blocks marked "unsafe"
  • These keywords cannot appear in a method with ref or out parameters
  • The yield return statement is not allowed inside a try-catch block, although it may be located inside a try-finally block.
  • A yield break statement may not be located inside a finally block.

You can download this sample code at DemoYield.zip (45.06 KB)


Mike Wood contributed to this article.