Click here to Skip to main content
Click here to Skip to main content

The Mystery Behind ‘Yield Return’

By , 14 Jul 2009
 

If you’ve been coding with .NET for awhile then you’ve most likely encountered a method that returns IEnumerable<T>. If you’re like me, you realize that this returns a list of items your can loop through and evaluate. If you ever dug into the code you may have found a funny looking return statement yield return.

It’s basically just an array, right? We’re just returning each value as a collection to loop over aren’t we? Well, it wouldn’t be the first time that someone, (as in myself) misunderstood the purpose of it but there really is a difference.

Let’s look at a fairly common example of returning collections.

The Usual Suspects

private string[] _Names = { "Joe", "Mary", "Bob" };

//a collection of names using a string array
public string[] GetResultsAsArray() {
    List<string> results = new List<string>();
    foreach (string name in this._Names) {
        Console.WriteLine("In GetResultsAsArray() : {0}", name);
        results.Add(name);
    }
    return results.ToArray();
}

//a collection of names using IEnumerable<string>
public IEnumerable<string> GetResultsAsEnumerable() {
    foreach (string name in this._Names) {
        Console.WriteLine("In GetResultsAsEnumerable() : {0}", name);
        yield return name;
    }
}

This is two common looking examples. The first is similar to what that joker on StackOverflow had said in the link above. The second uses yield return to return results.

So if we were to assign these values to a variable, what would our Console read?

var array = GetResultsAsArray();
var enumerable = GetResultsAsEnumerable();
Console.ReadKey();

In GetResultsAsArray() : Joe
In GetResultsAsArray() : Mary
In GetResultsAsArray() : Bob

Now, the first time I came across this I was shocked – I know I assigned my results — what happened?

As it turns out, there is this little thing called lazy evaluation in play here – Unless we need it, the method doesn’t get called. And since we aren’t using our results anywhere, the method is never actually executed.

Lazy Yet Eager

So we’ve determined that unless we use the results of our method, the method won’t ever be executed, which on one hand is actually really quite a handy feature.

Now here is another question for you, when we do use our results — what happens then? Consider this little bit of code.

Console.WriteLine("Array -- ");
foreach (string result in GetResultsAsArray()) {
    Console.WriteLine("In the array loop : " + result);
}

Console.WriteLine("\nEnumerable -- ");
foreach (string result in GetResultsAsEnumerable()) {
    Console.WriteLine("In the enumerable loop : " + result);
}

Console.ReadKey();

Array –
In GetResultsAsArray() : Joe
In GetResultsAsArray() : Mary
In GetResultsAsArray() : Bob
In the array loop : Joe
In the array loop : Mary
In the array loop : Bob

Enumerable –
In GetResultsAsEnumerable() : Joe
In the enumerable loop : Joe
In GetResultsAsEnumerable() : Mary
In the enumerable loop : Mary
In GetResultsAsEnumerable() : Bob
In the enumerable loop : Bob

Each time yield return sent a value back to our loop it was evaluated immediately! For being lazy code, this certainly was quick to give us our answers!

Lazy — And Maybe Late For The Party

Here’s an example where being lazy will get you into trouble, especially if you aren’t clear what it does.

IEnumerable<SearchResult> results;
using (SearchEngine search = new SearchEngine()) {
    results = search.GetEnumerableSearchResults();
}
foreach(SearchResult item in results) {
    //KABOOM!!
}

So what’s wrong with this code? I’ll tell you what – It’s that lazy, good-for-nothing call to GetEnumerableSearchResults(), that’s what!

Since our code doesn’t get evaluated until after we’ve already disposed our object, there isn’t anything there when the method is finally called! Looks like someone kicked into gear a little too late.

Of course code like this has many practical uses, so just make sure that you use it correctly and you’ll find it to be a valuable addition to your programming tool kit.

Don’t Trust Nobody… except your Mother*

So as it turns out there really is a difference between the two, and don’t let any of those fools on StackOverflow tell you otherwise, especially when that fool is me!  

*A saying my Italian grandfather used to say.

License

This article, along with any associated source code and files, is licensed under The Creative Commons Attribution-ShareAlike 2.5 License

About the Author

webdev_hb
United States United States
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5memberMVukoje28 Nov '12 - 22:37 
Finally cleared the confusion Big Grin | :-D
GeneralMy vote of 2memberLeonardo Paneque16 May '12 - 5:08 
lot of stuff missing, iterators, yield break, also "the mystery" is not there...
GeneralMy vote of 2memberErich Ledesma2 Apr '12 - 6:35 
I'm very sorry but this post does not give much information to clear the "mystery". You don't even mention Iterators. Yeah, is nice to talk about the lazzy evaluation, but that's only the tip of iterators.
 
http://msdn.microsoft.com/en-us/library/dscyy5s0.aspx
QuestionYour logic is flawed....membermikehell6816 Mar '12 - 4:15 
"Don't trust nobody, except your mother" = Trust everyone, except your mother.
 
Just saying.... Smile | :)
 
Nice posting thoughThumbs Up | :thumbsup:
GeneralMy vote of 5memberKenBonny13 Feb '12 - 1:44 
Very clear explenation of the yield command.
GeneralMy vote of 3memberLeandro Taset Blanco26 Dec '11 - 3:58 
I think you should have at least mentioned the
yield break;
statement.
GeneralMy vote of 5memberFrancisco Moreno Sanz14 Oct '10 - 0:06 
Very nice explanation
GeneralThank youmemberguyinfun14 Jul '09 - 5:34 
This sure was informative. Thanks a lot for explainingThumbs Up | :thumbsup:
You got my 5
QuestionSo is "yield return" a shortcut for returning the next item of an iterator?membersupercat914 Jul '09 - 5:28 
What versions of C# and .Net is it useful for? Is a comparable construct available in vb.net?
 
I've certainly written my share of iEnumerable types, so a simplified way of handling the creation of iEnumerator objects would be handy.
AnswerRe: So is "yield return" a shortcut for returning the next item of an iterator?memberphilippe dykmans20 Jul '09 - 7:30 
No. yield is more than a shortcut for an enumerator. The yield construct can make any method 'behave like' an enumerator, even if there is no real enumeration or enumerable in place.
 
A simple example. Let's make a class that returns multiples of 2:
 
public class MultipleOfTwo
{
int i = 1;
public IEnumerable Values
{
get { yield return (2 * i++); }
}
}
 
Now, a caller can say:
 
foreach (int value in new MultipleOfTwo().Values) { // do something with value }
 
In this example, yield is not returning the next item of an enumeration. It is returning a new result, and making it FEEL LIKE an enumeration. The good thing is that no multiples of 2 have to be precalculated to make them enumerable. And hence, that new values only need to be calculated as long as the caller is asking for them.
 
Philippe Dykmans
Software developpement
Advanced Bionics Corp.

GeneralRe: So is "yield return" a shortcut for returning the next item of an iterator?memberscott.leckie20 Jul '09 - 12:27 
Thanks Philippe - I have to say that you explained the concept much more clearly in a handful of lines than the article author did in two pages.
The article really needs an explanation of *why* you would use yield return.
GeneralRe: So is "yield return" a shortcut for returning the next item of an iterator?membersupercat921 Jul '09 - 7:30 
philippe dykmans wrote:
The yield construct can make any method 'behave like' an enumerator, even if there is no real enumeration or enumerable in place.

 
An iEnumerable can do much the same thing, can it not? I have an iEnumerable method, for example, which will return a sequence of numbers in a specified range, and another which will take an arbitrary number of iEnumerables as parameters and return the output from each in sequence. Thus, I can do something like:
  For I as Integer in MultiEnum(Range(0,10), Range(10,20,2), OneValue(100))
to have the loop run on 0, 1, 2, ..., 9, 10, 12, 14, 16, 18, 100. The Range function returns an iEnumerable whose enumerator simply counts as appropriate.
GeneralRe: So is "yield return" a shortcut for returning the next item of an iterator?memberphilippe dykmans23 Jul '09 - 12:56 
I assume you are talking VB.NET here. I am not familiar with that. But judging from your example, where i assume the intermediate values (like 1 to 9) are only computed if the caller asks for them, i'd say we may well be talking about the same concept. Yes.
 
There is another thing to mention about IEnumerable. I recently saw a piece of (extension) code that returned a 'Count' on an IEnumerable by just looping the enumeration and keeping a counter. The writer was probably assuming that all enumerations must somehow come to an end. That is only true if you enumerate lists, arrays... But if you enumerate a yield construct, the enumeration can well run FOREVER! In those cases it's up to the caller to decide when he's had enough of it and break out of the loop.
 
Yield is a very powerful construct if you want to make 'lazy' sequence generators. Like a sinewave, prime numbers, fibonacci sequence etc...
 
Philippe Dykmans
Software developpement
Advanced Bionics Corp.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130516.1 | Last Updated 14 Jul 2009
Article Copyright 2009 by webdev_hb
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid