Having fun with yield

I love C#. I’m not necessarily a fan of Microsoft or any company; I just think that, as a language, it’s far better than Java, its older cousin. Don’t get me wrong, I know that there is not one language to rule them all, and that it’s more about the framework and not the language. But, from a purely language feature point of view, C# is clearly much more expressive (unless you are biased against Microsoft) and it manages to combine multiple programming paradigms pretty well without being a mess (C++, I’m looking at you).

The LINQ library is one of its strongest features, but it wouldn’t be as amazing without the special keyword yield which it uses internally. I will show you how to use that to do fun stuff in C#, and how Unity takes advantage of its power to run co-routines.

Before I go on, I’ll just say something briefly about the IEnumerable and IEnumerator interfaces: IEnumerable is an interface which represents a sequence of data, and it is exactly what powers the foreach keyword, which expects types that implement the IEnumerable interface (such as List<T>). Moreover, using the keyword yield in a method requires for that method to have a return type of IEnumerable, IEnumerable<T>, IEnumerator  or IEnumerator<T>. IEnumerator is an interface which signifies a specific iteration over an IEnumerable. If this all is confusing, just remember this: IEnumerable is something that we can use  foreach  with.

How yield is used

For a past project (which never saw the light of day), I had to return an enumerable sequence after casting it to a base type. I couldn’t just cast the generic collection to the base equivalent (for example, it is impossible to cast List<Derived> to List<Base> where Derived is a class that derives from Base). Of course, for someone who doesn’t know C# that well, they would think that I have to either expose the original collection (which used a private class that I did not wish to expose), or create a new collection with everything already cast. Both solutions are bad, and the second one even even has a potentially huge overhead.

And this is where yield comes in: What the keyword does is that, put simply, it pauses the execution of a method, returns a single element from an iteration and then resumes the method when we need a new element. The LINQ library of C# comes with a Cast<T>()  method that does exactly what I want to do. This is not how it’s implemented in the standard library, but this is still a valid way to implement it:

Note: The this keyword before the parameter type signifies an extension method – it allows you to call Cast<T>() directly on an IEnumerable, as if the method belongs to it, without having to pass that IEnumerable as an argument.

When calling Cast<T>()  on a collection we’re not creating a new one. In fact, nothing actually happens just by calling that method. Consider this code, for example:

What does the following snippet print?

Even though DebugYield() is called before everything else is, it doesn’t output anything. What happens is that you tell the program to remember the start of that method. Its code only runs when you begin iterating, and pauses right when a yield return statement is met. It only resumes again when we need a new element. Therefore, this is what the final output looks like:

Lovely, isn’t it? But what does it mean exactly? Is this just a simpler and prettier way to iterate over a list? Well, it’s far more than that. It allows us to iterate over sequences that do not exist as a collection!


Consider this little method:

I could now do this:

Or even this (Sum is a predefined extension method in the System.Linq namespace):

This will return the result of 4950. Sure, you could use a for loop and do it manually, it’ll even run faster that way. But that is not the point.

Also, consider this neat little method:

Iterating through this IEnumerable will produce the same result as when iterating through an int array that contains the numbers 4, 8, 15, 16, 23 and 42, in that order.

How Unity uses yield

The snippet:

is just syntax sugar for this:

GetNext() moves the enumerator to the next element (it needs to be called at least once to get the first element), and the property Current simply returns the current element the enumerator is processing. Unity uses that and the yield keyword to process co-routines.

Say we need to write a method which will take too long to complete, so we would like to display a progress bar while that happens. If we just run the method, everything will freeze and then resume again when that method is finished. If we tried to display a loading bar, we’d only see it in two states: empty and full. Or, more likely, the game will freeze before the loading and then unfreeze again after that finishes, without displaying anything in between. This is not user friendly at all.

Of course, we could run all the processing in one thread and the drawing of a loading bar in another. Although that is intuitively a good idea, there is a problem: Unity is not very friendly with multi-threading, and this method would add a lot of extra complexity that isn’t really necessary. An easier way to do this is to use a co-routine.

Using a co-routine is simple. From within a MonoBehaviour, call:

The following code uses a list of a sample class called LoadableItem, which contains a method called Process() in which processing is supposed to happen. This could be, for example, dynamically generating a mesh in the scene.

If you attach this script on an object in an empty Unity project, you’ll notice that a rectangle bar fills up for a couple of seconds and then disappears. But wait, you say, Process() is only an empty method. How is it possible that going through 100 empty methods takes more than a second to finish?

Well, no need to be alarmed. Unity runs co-routines once for every update, so if we assume that Update() is called exactly 60 times per second, for example, going through every process will take 1.4 seconds. Which is, as you are righteously complaining, an overkill.

It makes no sense to have a loading bar when your calculations don’t take long to finish. This will always depend on your game and what it needs to do. But, even if you DO need a loading bar, 1.4 seconds of added time might be too much, especially if the loading itself should only take about a second. And this time will only increase the more items you need to process.

An easy way to get around it is to only pause the co-routine when enough time has passed. For really simple calculations, you won’t even see a loading bar. For more complex calculations, you will see one, but with a negligible added delay. Note that there’s actually no way to completely eliminate an overhead, even if we use multi-threading.

For the following code, I have also introduced some artificial delay which makes a Process() call last for 100 milliseconds and therefore makes the total loading time at least 10 seconds long. Changes are highlighted:

An UpdateLimit of 0.2 introduces about 80+ milliseconds of added delay for each second of actual loading time, assuming a fixed amount of 60 updates per second. Increasing the UpdateLimit reduces this delay, but might make the loading bar look less responsive.

One thing that is good about this solution is that if the loading takes less than UpdateLimit seconds (0.2 seconds or 200 milliseconds), no progress bar will be shown. Try commenting out line 20, which adds the artificial delay. You will not see a loading bar, but it’s not because it’s too fast for us to see; it never appears in the first place.

Another advantage is that the number of items has a negligible impact, so loading 1000 items that take 10 milliseconds each will take the same time as loading 100 items that take 100 millisecond each.

Note that the above code is in C#. UnityScript actually greatly simplifies the syntax of using co-routines, but the advantages of C# over UnityScript are far too many to choose UnityScript over this.

Using yield instructions

Unity provides some yield instructions that can be used for co-routines. Only three yield instructions are currently supported, with no possibility to extend them:  WaitForSeconds, WaitForFixedUpdate and WaitForEndOfFrame.

Assume we have a hero character with a possible health of between 0 and 200 hit points. Whenever he touches something toxic, he gets poisoned. Being poisoned means that he loses 10 hit points every second, over the period of 5 seconds.

Doing this in Unity is remarkably easy:

WaitForSeconds  produces a much different result from the   Thread.Sleep  method we encountered earlier. The game keeps playing just fine, but the co-routine itself will not continue unless a second passes by.

The other yield instructions,  WaitForFixedUpdate and WaitForEndOfFrame, can pause a routine until after a fixed update happens or until after the end of the frame, respectively. I’ve never found a use for those myself. Note that if you yield return  anything other than those three classes, it will just get ignored.

How co-routines work in the background

We can’t really know exactly the code of how it works in the background, but here is a very simple way to implement your own co-routine handler, although it lacks some of the extra sophistication of Unity’s (which also handles yield instructions).

Wait, what? Was that it? Yes! RemoveAll() expects a method which returns true for an element if it should be deleted or not. We pass a lambda function, which first runs our routine until it finds a yield statement. MoveNext() both runs our routine once and returns false if there is no longer any enumeration to be done, in which case our routine is finished. If it is, then it will be removed from the list.

In conclusion, the yield keyword is a very simple concept, but allows us to achieve a lot of awesome things. It has the power to pause and resume the execution for a method in a very safe way. This in turn makes it easy for us to iterate over modified versions of collections in such a way that neither is the original collection changed, nor a new one is created.

The latter powers LINQ, a powerful C# library which allows us to make efficient declarative statements, in which we tell the language what we want instead of how to get it. This greatly improves code readability and maintainability, while possibly enhancing abstraction and encapsulation as a side-effect.

As a final note, I don’t mean to say that LINQ doesn’t ever create extra collections. Sometimes that’s required. Also, some LINQ algorithms (like Except()) need to use secret temporary data structures in order to work efficiently.

Leave a Reply