I've seen the keyword "yield" N times, and I didn't know the power of this keyword until recently. I'll show some examples below that using "yield" to make your code more readable and perform better
To give you some quick overview of yield, I first want to show an example without using this keyword. The following code is very simple
IList<string> FindBobs(IEnumerable<string> names)
{
var bobs = new List<string>();
foreach(var currName in names)
{
if(currName == "Bob")
(currName);
}
return bobs;
}
Note here I use IEnumerable<string> as the parameter type and IList<string> as the return type. Generally speaking, I prefer to have a wider range in terms of parameter input types, but it is more strict in return types (translator's note: that is, use base classes or interfaces when input, and use subclasses or implementation classes when returning). For input, if you need to loop through foreach, using IEnumerable will make more sense. For the output (translator's note: that is, return), I use the interface to make the implementation part change. Here I want to save the caller the hassle of generating lists, so I chose list as the return type.
The problem is that my design is not linkable. Such a design requires a list as a return value. In terms of implementation, this list may not be large, but it is not necessary.
Now, let's take a look at doing this in the "yield" way, and then I'll explain how to use it and how it works.
IEnumerable<string> FindBobs(IEnumerable<string> names)
{
foreach(var currName in names)
{
if(currName == "Bob")
yield return currName;
}
}
In this version, we change the return type to IEnumerable and we use "yield return". Note that I no longer need to create a list, is it a bit confusing now? Don't worry, it will become easier and easier when understanding how it works.
When using the "yield return" keyword group, .net will generate a large string of pipeline codes for you, you can pretend that it is magic. When starting to loop in the called code (not list here), what happens on the implementation is that the function is called over and over again, but each time it continues to execute from the part that exited the last execution
Traditional execution methods
Calling functions
Function execution and return list
The call section uses the returned list
Yield's execution method
Calling functions
Caller requests item
Return to the next item
Go back to step 2
Although the implementation of yield execution seems a bit complicated, we end up only having to "pop up" one item at a time instead of creating the entire list and returning it.
For syntax, I personally think yield is more concise and performs better for the purpose of the transfer function (translator's note: that is, code readability), I use IEnumerable as the return type to notify the caller that it can be looped by foreach and return data, and the caller can now decide for themselves whether it is willing to store the return value in the list, even if this comes at the cost of performance.
In this simple example I provided, maybe you can't find a lot of benefits of using yield, however, you can save a lot of unnecessary work when the caller needs to untraverse all the content provided by the function. When you use yield when linking the method, the work (time) you can save may be multiplied.
Ayende already has great examples: using yield for a slick pipes & filters implementation, and he even talks about: version that is multi-threaded. This made me very interesting.
My initial reservation about yield was that using this keyword might lead to potential performance problems, but in fact, I have not found any information to explain the impact of yield on performance so far, and the part I mentioned above that improve performance is much greater than the part of the compiler overhead.