yield is a keyword that allows the current method to “stream” results to an iterator. In simple words, it allows you to return results on demand. Only recently have I discovered the upgrades this keyword has received with C# 8.0. I wanted to share them here. The yield keyword can be used in specific scenarios. These scenarios include 2 modules. The first module, called the generator, generates sequential data. The second module, called the iterator, sequentially processes the data. For example, let’s consider a method to generate a range of numbers that we want to use with an iterator.
Iteration over a list
Without using yield it’d look something like this.
In the above example, when the foreach iteration begins, it invokes the GetNumbersList method. This method generates a list of numbers to be processed. It then returns the whole list for the foreach to work on.
The output for the code in previous snippet would be something like this –

We can see that the whole number list is generated and stored in memory in a list, and then it is iterated on.
Though this is the most straight forward way, there is a downside to this. All the data generation happens upfront and the iterator needs to wait for all data to be fully available before processing. In addition, all the generated data is present in the memory even when we don’t need it post the processing. These two behaviors can sometimes cause significant memory overheads for an application.
Iteration over yield response
Yield to the rescue! Let’s look at an alternate way of implementing the method using the yield keyword.
In this implementation, the GetNumbersYield method to generate the numbers looks smaller and simpler compared to the previous method. We do not maintain any kind of list in this method.
Let’s understand how this works. Whenever the foreach iterator needs a new element from the method to work on, it executes an iteration of the method until it reaches the yield statement. The yield statement provides the next data element for the iterator to work on. Then the iterator processes it till it needs the next element to process. This process continues until the method completes all iterations or “yield break” is encountered (if any). For more info on yield return and yield break, you can have a look at this official documentation.
The output for the code in previous snippet would be something like this –

We can see the difference between this and the previous method. This method generates and processes the data in parallel whereas the previous method first generates all the data needed and then processes it. This eliminates the need for storage in a list and also doesn’t keep the processor waiting while the data is busy.
How does it improve performance?
In this scenario, we are doing trivial tasks like generating and printing the numbers. But the real-life applications to this might be where the process done by iterator has limitations on the number of elements processed in a time frame or the generator can only generate in a limited frequency. If we follow the first approach where we first generate all the data and then process it one after the other, it might slow down both the generation and the processing due to the limitations.
In contrast, by following the second approach, we alternate between the generator and processor, thereby allowing one to work while the other is waiting and improving the overall efficiency.
Is something missing?
Sadly, until before C# 8.0, there wasn’t a way to use yield easily with async (without running the async generation synchronously). This forced running all asynchronous logic in a blocking way with yield. But C# 8.0 introduced the IAsyncEnumerable construct which allows us to use a similar yield pattern with async methods (mainly generators). Let’s look at this in the next example.
Async Iteration over yield
In this example, we can see that the generator is having asynchronous code within it and the return type of the method is now IAsyncEnumerable. This allows the iterator to request for the async data in an on-demand fashion and thereby processing it asynchronously too. Also, notice the difference in the way that the foreach loop is written. The await in front of the foreach signifies that the iterated data is provided in an async manner.

For more information on IAsyncEnumerable you can look at this article. Please let me know your comments below.