Understanding the yield Keyword in C#

Understanding the yield Keyword in C#

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

  1. Stateful Iteration: The state of the method is preserved between iterations, including local variables and the current position in the control flow.
  2. 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.
  3. Automatic Enumerator Creation: It helps to avoid the manual implementation of the IEnumerator or IEnumerator<T> interface, simplifying the code.

Why and When to Use yield

  1. Simplifying Enumeration Logic:
    • Use yield when you need to create a custom iterator for a collection without implementing the entire IEnumerator interface.
    • It simplifies the code by handling the intricacies of the state machine under the hood.
  2. 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.
  3. 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<intGetEvenNumbers(int max

    for (int i0; i <= max; i++) 

        if(i2 == 0) 

            yield return i; 
        } 
    }
}

Infinite Sequence Generator:

public IEnumerable<intInfiniteIntegers() 

    int i0while (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.

Stephen

Hi, my name is Stephen Finchett. I have been a software engineer for over 30 years and worked on complex, business critical, multi-user systems for all of my career. For the last 15 years, I have been concentrating on web based solutions using the Microsoft Stack including ASP.Net, C#, TypeScript, SQL Server and running everything at scale within Kubernetes.