Wednesday, August 13, 2014

Yield Return

Introduction

You're all familiar with yield return by now right? You know, that yellow traffic sign that means you have to yield to oncoming traffic, then return from whence you spewed forth never to return. Wait, what's that you say? This is a .Net coding blog not a traffic blog? Oh right, so maybe I meant the yield return statement within C#. I'll admit that while I had heard of yield return before this week, and I had even read a little bit about it online, I didn't really understand what it was and what it was for. I don't know about you, but if I start to read about something and I can't get a feel for why I would use it, I care less about how to use it. So, in addition to showing you what yield return does and how to use it, I'll cover a useful scenario where you might want to use it.

What it Be

yield return <x> lets you return an enumerable type from a function call, 1 element at a time. If you're wondering how it lets you do so 1 at a time without losing it's place the next time you call, it's because .Net keeps track of your place so the next time you call the method within an iteration loop, it picks back up right after where you left off with the previous yield return statement. Here's a quick contrived example to help explain:

using System;
using System.Collections.Generic;

namespace BlogYield
{
    class Program
    {
        public static IEnumerable<int> GetRandoms(int count, int min, int max)
        {
            Random rnd = new Random();
            for (int index = 0; index < count; index++)
            {
                int random = rnd.Next(min, max + 1);
                yield return random;
            }
        }

        static void Main(string[] args)
        {
            foreach (int random in GetRandoms(10, 0, 100))
                Console.WriteLine(random);
            Console.ReadKey();
        }
    }
}



This is a full console application in C# that displays 10 random numbers in the range 0 to 100. The function GetRandoms returns an IEnumerable of type int, and proceeds to loop through count times producing random numbers. Notice that neat little bit of yield return w/in the loop? This means that the first time you execute the function, it will go through all the lines of the function in sequence; create the Random object, start the for loop, create the random number, return the first random number. Now the second time the function is called (within the loop in the Main function), the loop within GetRandoms continues on to the next iteration, a new random integer is set, and yield return is called with the next item. Nifty huh? I know there are better ways to accomplish this, a single for-loop being foremost in my mind, but this really does have some good uses. I promise! Let's look at one now that you know the mechanics.

Use Case: Merging Lists

Merging multiple lists that contain the same type can be a bit of a chore. In drops yield return to the rescue! Have a look at this example:

        public static IEnumerable<T> MergeLists<T>(params IEnumerable<T>[] lists)
        {
            foreach (var list in lists)
            {
                foreach (var item in list)
                    yield return item;
            }
        }

//...
//now we call it...
            var list1 = new List<int>();
            list1.Add(2);
            list1.Add(4);
            var list2 = new List<int>();
            list2.Add(0);
            list2.Add(100);
            foreach (var number in MergeLists<int>(list1, list2))
                Console.WriteLine(number);




As you can imagine, our output is 2, 4, 0, 100 (on separate lines). And, thanks to the magic of generic methods (I suppose I'll have to cover those in the blog at some point) and params, we can pass in any number of lists of any type, so long as they're all the same type and all the "lists" are enumerable. Pretty freakin sweet! Your original lists remain untouched, life is good.

[A quick note: I got this idea from here and just modified it a little to fit my own purposes. Thank you John K!]

What's Next?

Find some other uses for yield return and practice using it. I'm sure you'll think of something if you try. Or, another good exercise might be to try to recreate the above MergeLists method on your own. Even if you don't use a generic method or the params keyword, it will still help cement your understanding of yield return. Lastly, check the resources at the bottom of the article, especially the yield C# reference. They go into a bit more detail about how yield return works than I did, as I just glossed over it so I could get to a practical example quickly.

Resources

A Practical Use of "yield" Keyword in C#
yield (C# Reference)

No comments:

Post a Comment