Working with lists
Understanding the power of yield return
Using the yield return for returning one item at a time
Hi, Earthlings!
Today I want to show you both ways of manipulating and returning lists from C# methods.
First I’ll demonstrate what is the “conventional” way of doing that by creating an empty list and filling it with the desired information for then returning it.
The second way is by working with yield return
, a clean way of working with enumerations and having cleaner methods by returning one item at a time.
The conventional way
The conventional way of returning a list of values is to initialize an empty list first, then iterate through the values you want and add them to the list.
Once you finish the operations necessary and added all the values you wanted to the list, you just return it.
With this method, you’ll always build the entire list first, and then return it to whoever is calling it.
Let’s say you have a method that you will get a list of numbers and return a list of the double of these numbers. This is how you would do it the conventional way:
Now, to print these lists we just have this method. Just a normal function that iterates through a list of numbers and prints it out with a ,
after it.
And now, if you want to stop manipulating the list, you’d just add a break
statement into the loop depending on a condition. Like the example below:
Yield contextual keyword
Another way of returning a list is through the keyword yield return
. Here, instead of returning the whole list, it always returns an enumeration and only executes the logic for a list member once it is iterated through. So, basically, instead of returning the full list of items, it returns one item at a time.
The advantage of this method is that you only process logic in an iteration once it’s its turn in the iteration (in a foreach
, for example).
You’d do this like:
And now you would just run it like:
Note that the result of PrintNumbers
is not
2, 4, 6, 8, 10, 12, 14, 16, 18, 20,
But it is
*2, *4, *6, *8, *10, *12, *14, *16, *18, *20,
The *
is inserted when GetDoubledNumbers
iteration runs and not when you call it. This is because when we call GetDoubleNumbers
, the code is not executed in the right away. It’s only executed for each iteration once the foreach
loop starts returning one item at a time.
Because of that, also exceptions will only be thrown once you iterate through it. So debugging might get a little confusing.
Another thing to keep in mind is that you cannot wrap the yield return
statement in a try-catch
block. If you want to have exception handling, you must wrap the logic you want in a try-catch
block before calling yield return
. Like the following code:
Yield break
Now, if you have a loop but want to stop it depending on a condition defined by you, you would normally use the break
keyword in a normal loop.
You can, of course, use it when using yield return
, but that would happen to only break the loop and would run the whole code below it.
Note that *End Method*
is also outputted. That’s because, with only the break
keyword, you stop the foreach
loop and not the whole enumeration at once.
If you want to just stop the enumeration from executing, you must use yield break
you can break the whole iteration from inside your method.
Async
And last, if you want to handle any asynchronous code in an enumeration method, you cannot just return Task<IEnumerable<T>>
. If you try to do so, you’ll get the following warning:
The body of 'GetDoubledNumbersIfMoreThanThreshhold(List<int>, int)' cannot be an iterator block because 'Task<IEnumerable<int>>' is not an iterator interface type
That is because now you are creating and consuming an asynchronous enumeration stream, which doesn’t work with returning this task.
To be able to work properly, you should return an IAsyncEnumerable<T>
that is the return type of an asynchronous stream. This basically means that each iteration will be called when requested.
And to iterate through, you won’t be using the standard foreach
loop, but will be adding an await
before it, making the loop be called await foreach
.
So, to have an async code properly running you can do like the code below:
And it is very interesting to see this code in action:
You can see how the numbers are printed once their iteration time comes.
Conclusion
We’ve gone through the difference between the two methods of building and returning a list of items through a C# method.
We’ve explored the power of yield return
and how it can keep your code clean. But you also saw how you should use it carefully because it won’t return the whole list of items once your method is called, but instead will return each item at a time once you start iteration through the resulting enumeration.
And last, you could learn how to work with asynchronous code if you want to use yield return
by returning an IAsyncEnumerable
and working with await foreach
loop.
The example code for this article can be found here.