Yield Return in C#

In c# you can use yield return instead of declaring a collection, adding to it then returning it all at once. The code ends up looking a lot cleaner as you can see in the examples below.


	private static IEnumerable<double> StopwatchExampleYield()
	{
		for (int i = 0; i < 3; i++)
		{
			Thread.Sleep(TimeSpan.FromSeconds(1));
			yield return _stopWatch.Elapsed.TotalSeconds;
		}
	}
	private static IEnumerable<double> StopwatchExampleNoYield()
	{
		var results = new List<double>();
		for (int i = 0; i < 3; i++)
		{
			Thread.Sleep(TimeSpan.FromSeconds(1));
			results.Add(_stopWatch.Elapsed.TotalSeconds);
		}
		return results;
	}

Two functions that look like they're functionally the same except the one with yield return is nice and short.

Although they look similar and act similar most of the time there is a difference. Yield return will delay execution of the function until the returned value is enumerated. Below the first method is called, followed by the second without yield return. Then the result of the NoYield() function is enumerated and the results displayed. If both functions worked the same the delay between the foreach loops wouldn't affect the output, but it does because the function with yield is only evaluated then.

	static double secondsToWait = 5;
	static void Main(string[] args)
	{
		_stopWatch.Start();
		Stopwatch methodStopwatch = new Stopwatch();

		Console.WriteLine("Calling StopwatchExampleYield()");
		methodStopwatch.Start();
		var yield = StopwatchExampleYield();

		Console.WriteLine("Done in {0} seconds.", methodStopwatch.Elapsed.TotalSeconds);

		Console.WriteLine("Calling StopwatchExampleNoYield()");
		methodStopwatch.Restart();
		var noYield = StopwatchExampleNoYield();
		Console.WriteLine("Done in {0} seconds.", methodStopwatch.Elapsed.TotalSeconds);

		Console.WriteLine("Waiting {0} seconds.",secondsToWait);
		Thread.Sleep(TimeSpan.FromSeconds(secondsToWait));
		Console.WriteLine("StopwatchExampleYield() results:");
		methodStopwatch.Restart();
		Console.WriteLine(string.Join("\t", yield));
		Console.WriteLine("Yield written in {0} seconds.", methodStopwatch.Elapsed.TotalSeconds);

		Console.WriteLine("StopwatchExampleNoYield() results:");
		methodStopwatch.Restart();

		Console.WriteLine(string.Join("\t", noYield));
		Console.WriteLine("No Yield written in {0} seconds.", methodStopwatch.Elapsed.TotalSeconds);

		Console.WriteLine("Done in {0} seconds.", methodStopwatch.Elapsed.TotalSeconds);
		Console.ReadKey();
	}

Main method to illustrate the difference.

	Calling StopwatchExampleYield()
	Done in 0.0005714 seconds.
	Calling StopwatchExampleNoYield()
	Done in 3.000821 seconds.
	Waiting 5 seconds.
	StopwatchExampleYield() results:
	9.0040594       10.0041546      11.0042847
	Yield written in 3.0013281 seconds.
	StopwatchExampleNoYield() results:
	1.0018417       2.0019242       3.0020194
	No Yield written in 0.0001203 seconds.
	Done in 0.0001945 seconds.

Program output.

In the call to the NoYield version it waits 1s before adding to the list so you get roughly 1, 2 and 3 seconds. Then there's the 5 second delay and the Yield version gets called when it's used as an argument to string.Join(). So it's 9, 10 and 11 seconds because the NoYied version takes 3, the delay is 5 and inside the Yield() method the 1 second delay comes first.

comments powered by Disqus