I'm new to C#, have not seen the equivalent of yield in previous languages I've tried to learn, and am not convinced that it is helpful except perhaps for readability. I survived all these years without it, so why do I need it?
As I undersand, you can use yield return to spit out values of type T one-by-one rather than collecting those values into an IEnumerable<T> and spitting that whole collection out at the end. What's the point? After all, I'm sure there is some overhead involved in interrupting the execution of the function to copy out a single value. Perhaps I'll run some performance tests to see if it's more efficient in terms of time. More than that, I'm wondering if you can show me a specific situation where I would need to iterate through a set of values collected by a function and can only do it with yield or would be better off doing it with yield.
As a marquee example of iterator usage, consider a number series iterator:
IEnumerable<int> fibo() {
int cur = 0, next = 1;
while(true) {
yield return cur;
next += cur;
cur = next - cur;
}
}
Now we can choose what to do with the series, and only the required elements are calculated:
var fibs = fibo();
var sumOfFirst10Fibs = fibs.Take(10).Sum();
Another useful pattern is flattening a complex data structure, like a tree1:
public class Tree<T> {
public Tree<T> Left, Right;
public T value;
public IEnumerable<T> InOrder() {
if(Left != null) {
foreach(T val in Left.InOrder())
yield return val;
}
yield return value;
if(Right != null) {
foreach(T val in Right.InOrder())
yield return val;
}
}
}
}
1 As noted by Alexey in the comments, the in-order traversal is inefficient (particularly when tall trees are traversed).
The idea is to generate the values on the fly. Your collection of values might be infinite or the cost of generating each value might be high. When you foreach through an IEnumerable, you are actually calling methods on IEnumerator, which can be implemented in any way you like. A function that uses yield is automatically reimplemented as an IEnumerator that generates values only when they are requested. When you want to generate values on the fly as well, you also have to code an implementation of IEnumerator just like the one a yielding function is replaced with.
Some specific situations where using a generator might be preferable to creating and returning a collection:
yield return it. You can write a loop, of course, but by extracting the logic into a generator you can easily replace the file with a database table or a file in a different format, for exampleBasically, in every case where you do not have to worry about handling IEnumerable as ICollection, i.e. you treat is as a stream of values, not as a finite bag of values with a Count, you might save time or memory by using a generator.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With