Glossary / yield return
yield return
yield return emits one value from an iterator and suspends execution while preserving local state for the next enumeration step. It supports lazy sequences and async streams, enabling consumers to process data incrementally and stop early without requiring a full collection.
State machine and control flow
static IEnumerable<string> ReadNames()
{
yield return "Ada";
yield return "Grace";
}
The compiler generates an enumerator state machine. Each MoveNext() advances until the next yield or completion. yield break ends the sequence. The iterator body is typically deferred until enumeration begins. using resources inside the iterator remain alive across yields and are disposed when enumeration completes or the enumerator is disposed; a consumer that abandons enumeration must dispose the enumerator, which foreach does automatically.
Yield is restricted in certain exception-handling blocks and cannot be used in some contexts involving ref safety; consult the language version specification. Async iterators use yield return with IAsyncEnumerable<T> and can await between values.
Engineering guidance
Use yield to avoid unnecessary buffering, but document that each enumeration may repeat work. Avoid holding scarce resources across arbitrary consumer pauses unless that is intentional. Test early break, exception, cancellation (for async streams), and repeated enumeration.
Interview questions
- What happens at a yield? A value is returned and execution state is suspended.
- Is the whole sequence computed at call time? Usually no; computation is deferred to enumeration.
- How is cleanup handled on early exit? Enumerator disposal runs iterator cleanup/finally blocks.
- What can surprise callers? Side effects repeat on re-enumeration and exceptions happen during enumeration.
- How does async yield differ? Async iterators can await between elements and expose
IAsyncEnumerable<T>.