TheDeveloperBlog.com


C# Yield Return Example

Yield interacts with the foreach-loop. It is a contextual keyword: yield is a keyword only in certain statements. It allows each iteration in a foreach-loop be generated only when needed. In this way it can improve performance.


Example. We use the yield keyword in several contexts. We use it in methods that return the type IEnumerable (without any angle brackets), or the type IEnumerable<Type> with a type parameter in the angle brackets.

Tip: We reference the System.Collections namespace for the first version, and the System.Collections.Generic namespace for the second.

Collections
C# program that uses foreach, yield return

using System;
using System.Collections.Generic;

public class Program
{
    static void Main()
    {
	//
	// Compute two with the exponent of 30.
	//
	foreach (int value in ComputePower(2, 30))
	{
	    Console.Write(value);
	    Console.Write(" ");
	}
	Console.WriteLine();
    }

    public static IEnumerable<int> ComputePower(int number, int exponent)
    {
	int exponentNum = 0;
	int numberResult = 1;
	//
	// Continue loop until the exponent count is reached.
	//
	while (exponentNum < exponent)
	{
	    //
	    // Multiply the result.
	    //
	    numberResult *= number;
	    exponentNum++;
	    //
	    // Return the result with yield.
	    //
	    yield return numberResult;
	}
    }
}

In Main, we see one foreach loop. In the part of the foreach loop following the "in" keyword, there is a method call to ComputePower, which you can see also in the Program class. Two ints are passed as arguments to ComputePower.

Foreach

ComputePower receives two parameters. The first is the number integer. The second is the number of numbers you want to receive. This method returns an IEnumerable<int> type, which we can use in a foreach-loop.

IEnumerable is an interface describing a type that implements the internal methods required for using foreach-loops. The ComputePower method uses the simple mathematical logic necessary for finding the exponential numbers.

Finally: We see the yield return contextual keyword, in the while iterative statement enclosing block.

While

Return. Yield return is semantically equivalent to multiple statements. It is similar to a return statement (which passes control flow to the calling method), followed by a "goto" to the yield statement in the next iteration of the foreach-loop.

ReturnGoto

This behavior does not exist in the Common Language Runtime. It is implemented by a class generated by the C# compiler. This is then executed and JIT-compiled by the CLR. Yield is a form of syntactic sugar.


MSDN. Microsoft provides a method that is essentially equivalent on their yield C# Reference page. But their implementation uses the non-generic IEnumerable type. This introduces some inefficiencies.

Note: It likely results in the integers being boxed, because IEnumerable does not have knowledge of their type and instead acts on Object.

Yield: MSDN

Benchmark. We look at how the method offered on MSDN compares to the method presented here. The method developed here has explicit knowledge of the value type in the IEnumerable collection, which is more informative to the CLR at runtime.

So: The performance is much better. And the actual result values of the methods are the same.

Loops benchmarked: C#

// [MSDN]
foreach (int value in Power(2, 30))
{
}

// [DOT NET PERLS]
foreach (int value in ComputePower(2, 30))
{
}

Benchmark notes
    1 million iterations of each loop tested.
    Code samples have same computational result.

Simulation results for yield keyword usage

MSDN yield return/IEnumerable: 403 ms
Yield return/IEnumerable<int>: 224 ms [faster]


Internals. The C# code you have that uses yield is never actually executed by the CLR at runtime. Instead, the C# compiler transforms that code before the runtime ever occurs. This occurs within the C# compiler.

The compiler-generated class, marked with CompilerGenerated, instead uses several integral types. The result is an entire class that is similar to how your code would look if you manually implemented the interfaces.

Compiler-generated class: C#

// Nested Types
[CompilerGenerated]
private sealed class <ComputePower>d__0 : IEnumerable<int>,
    IEnumerable, IEnumerator<int> // ...
{
    // Fields
    private int <>1__state;
    private int <>2__current;
    public int <>3__exponent;
    public int <>3__number;
    private int <>l__initialThreadId;
    public int <exponentNum>5__1;
    public int <numberResult>5__2;
    public int exponent;
    public int number;
    // Methods [omitted]
}

The actual class implements several more interfaces. It has several methods. The punctuation characters allow the compiler to ensure no naming conflicts will occur with your code.


Uses. Many uses exist for the yield keyword. For example, you can use it as a filter for displaying random HTML code in your ASP.NET website. Internally, you can select random elements from an array and yield them.


Summary. We examined the yield return pattern. We rewrote an example from the MSDN article, resulting in superior performance. The yield keyword is contextual—it only has special significance in some places.

And: We benchmarked the IEnumerable and IEnumerable generic interfaces. We saw how the C# compiler generates the implementation.