C yield return range/collection
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
yield return in C# creates iterator methods that produce elements one at a time without building an entire collection in memory. When iterating over a range or collection, yield return lazily generates each element on demand — the method pauses after each yield and resumes when the next element is requested. This is ideal for large sequences, infinite generators, and transformation pipelines where materializing the full collection would waste memory.
Basic yield return
The compiler transforms the method into a state machine. Each yield return saves the method's position and returns a value. The next iteration resumes from where it left off.
Generating a Range
This is equivalent to Enumerable.Range(5, 4) but demonstrates how yield return works with loops. Only one value exists in memory at a time.
Yielding from a Collection
The input collection is iterated lazily — if you break out of the foreach, the remaining items are never processed.
yield return vs Returning a List
Chaining Iterators
This is exactly how LINQ works internally — each LINQ method returns a lazy IEnumerable using yield return.
yield break
yield break ends the iterator immediately:
Infinite Sequences
Without yield return, an infinite sequence would require an infinite loop that never returns. Lazy evaluation makes it practical.
Async Iterators (C# 8+)
IAsyncEnumerable<T> combines yield return with await, enabling lazy asynchronous iteration.
Common Pitfalls
- Deferred execution surprises: Code inside an iterator method does not run until the first
MoveNext()call. If the method has parameter validation, the exception is not thrown until iteration begins, not when the method is called. Validate parameters in a non-iterator wrapper that calls the private iterator. - Multiple enumeration: Each
foreachover anIEnumerablereturned byyield returnre-executes the entire method from the start. If the method reads from a database or file, it reads again. Call.ToList()to materialize the results when multiple iterations are needed. - Using yield return in try/catch:
yield returncannot appear inside atryblock that has acatchclause. It can appear in atryblock with only afinallyclause. This is a compiler limitation due to how the state machine is generated. - Forgetting that yield return keeps the method alive: Local variables in a
yield returnmethod are not garbage collected until iteration is complete or the enumerator is disposed. For methods that holdIDisposableresources, usetry/finallywithyield returnto ensure cleanup. - Returning IEnumerable but calling .Count() or .ToList() immediately: If the caller always materializes the sequence with
.ToList()or.Count(), the lazy evaluation provides no benefit. Useyield returnwhen consumers typically iterate partially or chain with other LINQ operations.
Summary
yield returncreates lazy iterators that generate elements one at a time- Only one element is in memory at a time — ideal for large or infinite sequences
- The compiler transforms the method into a state machine that pauses and resumes
yield breakterminates the iterator early- LINQ methods (
Where,Select,Take) useyield returninternally - Async iterators (
IAsyncEnumerable<T>) combineyield returnwithawaitin C# 8+ - Validate parameters in a non-iterator wrapper to get immediate exceptions

