IEnumerable and Yield
March 23rd, 2013 | Published in .net
A couple of weeks ago I sent an email to my team asking them some questions around the yield keyword. Normally when I send these weekly emails I make sure they are challenging but for whatever reason my brain wasn’t working a couple of weeks ago so thought I would give them an easy week.
I was a little surprised to learn that although a couple of the team knew what yield was and how it should be used a number of other team members didn’t, we had members who had not heard of yield before.
This was a little bit of a surprise to me, but given that this was the case I thought it might make a good blog post, This is nothing more than a brief introduction.
For more detail on iterators look at Jon Skeet’s C# in depth. You can get the chapter on iterator for free from manning here
What is yield and why you should care.
Basically yield is just a little syntax sugar, it implements an iterator and returns a single item, on the next call it gets the next item with movenext and returns it.
This is advantageous when you calculate the items in the list or if the list returns an infinite set, example might be a set of primes or random numbers. You can never return the whole set so you return it incrementally.
Random numbers are a good example.
Say we have a method called GetRandomNumbers(int maxCount)
public static IEnumerable GetRandomNumbers(int maxCount)
{
var rand = new Random();
for (int i = 0; i < maxCount; i++)
{
Thread.Sleep(500);
yield return rand.Next();
}
}
I have set the method to sleep for 500ms on each loop to add a little bit of calculation time to the items in the loop.
Next I added another method that returns an IList like this:
public static IList GetRandomNumberList(int maxCount)
{
var numberList = new List();
var rand = new Random();
for (int i = 0; i < maxCount; i++)
{
Thread.Sleep(500);
numberList.Add(rand.Next());
}
return numberList;
}
Essentially both methods do the same thing, get N random numbers. The way they do it and the affect that has on your UI and both the perceived and actual performance of your application is striking and something that you should be aware of.
To really see this for yourself run the following console app: https://gist.github.com/david-mclean/5228317
Although the yield example was actually 167ms slower, It looked and felt like it was faster.
This perception of performance and the feedback an application gives the end user is sometimes more important than the application being 167ms faster.
Another example, this time where yield is more performant than list.
This time we will have methods GetRandoms and GetRandomsList : https://gist.github.com/david-mclean/5228693
These methods will each return 5,000,000 random numbers.
In this example I have a need for only 10 random number, so I use the Take extension method.
So my calls look like this:
GetRandoms().Take(10);
GetRandomsList().Take(10);
Now when I run the code I get the following result:
GetRandoms: 1ms
GetRandomsList: 123ms
In this instance GetRandomsList had to get all 5,000,000 results before Take could get the first 10 items. GetRandoms on the other hand only returned 10 items, linq uses yield for a lot of what it does, that is why the return type for a lot of the extensions are IEnumerable.
Should you use yield.
Well that depends on what you are looking to achieve. In cases where you are updating a UI and the background process has to do some intensive work for each item then its worth considering.