C#advanceddotnet

Iterators with yield

An iterator method that hands back one value at a time with yield return, building a lazy IEnumerable<T> the runtime resumes on demand instead of materialising a list.

yield return produces the next element and parks the method until MoveNext asks again; yield break ends the sequence early, so a foreach can consume an infinite stream and stop after Take(n).

What an iterator method is

Any method whose return type is IEnumerable<T> (or IEnumerator<T>) and whose body contains a yield return is an iterator method. The compiler rewrites it into a hidden state machine: each yield return produces one element and suspends the method, preserving every local variable and the exact position in the code. The next time the consumer asks for an element, execution resumes on the line after that yield return.

Nothing runs when you call the method β€” it only hands back an enumerable. The body starts executing on the first MoveNext, which is what foreach calls under the hood. This is why iterators are called lazy: work happens element-by-element, pulled by the consumer, not eagerly up front.

Why laziness matters

Because elements are produced on demand, an iterator can describe a sequence far larger than memory β€” or one with no end at all. An iterator that loops forever with yield return is perfectly valid; it simply never returns control unless the consumer stops asking. Pairing such a stream with LINQ's Take(n) gives you the first n elements and then disposal halts production. The infinite loop never actually runs forever because Take stops pulling.

Laziness also means a foreach over a filter-style iterator interleaves with the source: each value is generated, tested, and yielded one at a time, so you never build an intermediate collection.

Ending early with yield break

A plain return; is illegal in an iterator body β€” there is no single value to return. Instead, yield break ends the sequence immediately: the enumerator reports "no more elements" and the foreach stops. Use it as a guard (bail out before producing anything) or mid-stream (stop once some condition is met). Reaching the end of the method body has the same effect implicitly.

Try it 4 examples

A range iterator

C#intro
0ms
5678
Reference Β· output compiled offline, not runnable in-browser

Each yield return start + i hands back one value (5 through 8) and parks the loop until foreach calls MoveNext again, so the four numbers are produced lazily rather than from a pre-built list. This is the minimal shape of an iterator method: a normal for loop whose yield return the compiler turns into a resumable state machine.