Yield Statement
The yield statement lets a method produce sequence values one at a time instead of building the entire result up front. This is one of the clearest examples of lazy execution in C#.
That matters because sometimes you do not want to create a whole collection immediately. You want to describe how values are produced and let callers consume them as needed.
The main idea
An ordinary method usually finishes all its work before returning. A method that uses yield return can pause after producing one value, then resume later when the caller asks for the next value.
static IEnumerable<int> CountToThree()
{
yield return 1;
yield return 2;
yield return 3;
}This does not mean the whole sequence is built immediately in memory. It means the method defines an iterator.
Lazy iteration flow
flowchart TD
A["Caller starts foreach"] --> B["Iterator method begins"]
B --> C["Run until first yield return"]
C --> D["Return one value to caller"]
D --> E{"Caller asks for another value?"}
E -- Yes --> F["Resume after previous yield"]
F --> C
E -- No --> G["Iteration ends"]This is the most important mental model for yield: the method pauses and resumes.
yield return
yield return produces one value in a sequence.
static IEnumerable<string> GetNames()
{
yield return "Ava";
yield return "Liam";
yield return "Mina";
}Each time the caller asks for the next item, the iterator resumes until it reaches the next yield return.
yield break
yield break ends the iteration early.
static IEnumerable<int> GetPositiveNumbers(int[] values)
{
foreach (int value in values)
{
if (value < 0)
{
yield break;
}
yield return value;
}
}Once yield break runs, the sequence ends.
A worked example
Suppose you want a method that returns even numbers from 1 through a chosen limit.
static IEnumerable<int> GetEvenNumbers(int limit)
{
for (int i = 1; i <= limit; i++)
{
if (i % 2 == 0)
{
yield return i;
}
}
}
foreach (int number in GetEvenNumbers(10))
{
Console.WriteLine(number);
}Notice what the iterator method is doing:
- it does not create a
List<int> - it does not need to store all even numbers first
- it yields each value at the moment it is discovered
This is often a more natural way to express sequence generation.
Why yield is useful
yield is especially useful when:
- the sequence may be large
- values can be generated one by one
- callers may stop early
- you want iteration logic without manually implementing
IEnumerator
It keeps sequence-generation code readable while still enabling deferred execution.
A useful mental model
Think of an iterator method as a machine that knows how to produce the next item whenever asked.
It does not necessarily do all the work immediately. It does just enough work to produce the next value.
Common mistakes
- Assuming the whole sequence runs immediately when the method is called.
- Forgetting that side effects inside iterator methods may happen later during enumeration, not at method call time.
- Using
yieldwhen a simple prebuilt collection would be clearer and perfectly small. - Expecting
yield returnto behave like a normalreturn. It returns one item, not the final result of the whole method.
Summary
The yield statement makes lazy iteration simple to express.
The main ideas are:
yield returnproduces one item at a time- the method pauses and resumes during enumeration
yield breakends the sequence- iterators are useful for generating or filtering sequences lazily
This is one of the most elegant control-flow features in C# because it changes not only what code does, but when that code does it.
Practice
Write an iterator method that yields the numbers 1 through 5.
As a second exercise, write an iterator that yields only words longer than three characters from an array of strings.