Introduction
In C#, the yield
keyword is used in the context of iterator methods. It plays a crucial role in customising the iteration behavior of a collection or series of values.
This article explains what the yield
command does and provides insight into when and why it should be used.
What Does yield
Do?
The yield
keyword is used in methods that return an IEnumerable
, IEnumerable<T>
, IEnumerator
, or IEnumerator<T>
.
It enables you to return one element at a time, pausing the execution of the method after each yield return
statement, and resuming from that point the next time the iterator method is called.
Key Characteristics of yield
- Stateful Iteration: The state of the method is preserved between iterations, including local variables and the current position in the control flow.
- Deferred Execution: The method execution is deferred until the returned sequence is iterated over. This means the method isn’t executed when it’s initially called, but when its elements are enumerated.
- Automatic Enumerator Creation: It helps to avoid the manual implementation of the
IEnumerator
orIEnumerator<T>
interface, simplifying the code.
Why and When to Use yield
- Simplifying Enumeration Logic:
- Use
yield
when you need to create a custom iterator for a collection without implementing the entireIEnumerator
interface. - It simplifies the code by handling the intricacies of the state machine under the hood.
- Use
- Handling Large or Infinite Sequences Efficiently:
yield
is particularly useful for sequences that are computationally expensive or infinite.- Since execution is deferred and stateful, you can efficiently generate values on-the-fly as they are requested, instead of creating and storing the entire collection in memory.
- Custom Sequence Generation:
- Create custom sequences or iterate over a collection in a non-standard way (e.g., filtering, modifying, or skipping elements).
Examples
Generating a Sequence of Numbers:
public IEnumerable<int> GetEvenNumbers(int max)
{
for (int i = 0; i <= max; i++)
{
if(i % 2 == 0)
{
yield return i;
}
}
}
Infinite Sequence Generator:
public IEnumerable<int> InfiniteIntegers()
{
int i = 0; while (true)
{
yield returni++;
}
}
Best Practices
- State Preservation: Remember that local state is preserved between iterations, but not between different enumerators.
- Exception Handling: Be cautious with exception handling; exceptions in the iterator block are deferred until the returned sequence is iterated.
- Thread Safety: As with any IEnumerable, consider thread safety when iterating over shared or mutable data.
- Deferred Execution: Be aware that the execution is deferred. Side effects, if any, won’t occur until the enumerator is actually iterated.
Conclusion
The yield
keyword in C# offers a convenient and powerful way to create custom iterators for collections and sequences.
By understanding and leveraging yield
, developers can write more efficient and readable code for generating and processing sequences of data, especially when dealing with large, complex, or infinite collections.