What does yield return compile into?
Into a state machine class. The compiler rewrites your method into a hidden type that implements IEnumerable<T> and IEnumerator<T>, and every yield return becomes a point where MoveNext() saves its position and returns true.
Question
What does yield return compile into?
What It Is
When a method contains yield return, its body no longer runs when you call it. Calling it just creates the state machine object. The code only starts executing on the first MoveNext(), runs until the next yield return, stores the current value and the current "state" (which yield it stopped at, plus the values of your local variables, now turned into fields), and returns.
That design is what makes iterators lazy: you can describe an infinite sequence and only pay for the elements someone actually consumes.
The Surprises
Lazy execution has two side effects that catch people:
- Argument validation is deferred. A
throwat the top of the iterator won't happen when the method is called, only when someone starts enumerating, possibly far away in the code. - Every enumeration runs the code again. Enumerating the same
IEnumerable<T>twice (for example, calling.Count()and thenforeach) executes the whole body twice, including any database or file access inside it.
Practical Example
csharpstatic IEnumerable<int> Countdown(int from)
{
if (from < 0) throw new ArgumentOutOfRangeException(nameof(from));
for (int i = from; i >= 0; i--)
yield return i;
}
var seq = Countdown(-1); // no exception yet!
foreach (var n in seq) { } // ArgumentOutOfRangeException thrown here
// Validate eagerly, iterate lazily
static IEnumerable<int> SafeCountdown(int from)
{
ArgumentOutOfRangeException.ThrowIfNegative(from); // runs at call time
return Iterate();
IEnumerable<int> Iterate()
{
for (int i = from; i >= 0; i--)
yield return i;
}
}Solution and Conclusion
Use iterators when laziness is what you want: streaming, pipelines, potentially large or infinite sequences. Split validation into a non-iterator wrapper with a local iterator function, and materialize with .ToList() when you need to enumerate the result more than once.