I often find the scenario where I return an IEnumerable using the yield return
statement, then have other methods which call this function with different parameters and directly return the result.
Is there any performance benefit to iterating through the results end yield return
ing these as opposed to simply returning the collection? i.e. if I use return
on the resulting IEnumberable rather than looping through these results again and using yield return
does the compiler know to only generate the results as they're required, or does it wait for the entire collection to be returned before returning all results?
public class Demo
{
private IEnumerable<int> GetNumbers(int x)
{
//imagine this operation were more expensive than this demo version
//e.g. calling a web service on each iteration.
for (int i = 0; i < x; i++)
{
yield return i;
}
}
//does this wait for the full list before returning
public IEnumerable<int> GetNumbersWrapped()
{
return GetNumbers(10);
}
//whilst this allows the benefits of Yield Return to persist?
public IEnumerable<int> GetNumbersWrappedYield()
{
foreach (int i in GetNumbers(10))
yield return i;
}
}
GetNumbersWrapped
simply passes through the original enumerable - it hasn't even invoked the iterator at that point. The end result remains fully deferred / lazy / spooling / whatever else.GetNumbersWrappedYield
adds an extra layer of abstraction - so now, every call toMoveNext
has to do ever so slightly more work; not enough to cause pain. It too will be fully deferred / lazy / spooling / whatever else - but adds some minor overheads. Usually these minor overheads are justified by the fact that you are adding some value such as filtering or additional processing; but not really in this example.Note: one thing that
GetNumbersWrappedYield
does do is prevent abuse by callers. For example, ifGetNumbers
was:then callers of
GetNumbersWrapped
can abuse this:However, callers of
GetNumbersWrappedYield
cannot do this... at least, not quite as easily. They could, of course, still use reflection to pull the iterator apart, obtain the wrapped inner reference, and cast that.This, however, is not usually a genuine concern.