Everything you need to know about Yield in C#
Boost memory efficiency by generating sequences on demand with yield in C# and code like a pro 🐿️

Hey devs, this time we’ll learn about the yield statement! I've seen it pop up in technical interviews, so if you’re not familiar with it yet, don’t worry – you’ll leave here with a solid understanding 🐿️🤭.

When coding, we often need to work with data collections, like lists or arrays, which can be quite large. Imagine you have a list of thousands of numbers, but you only want to process one at a time – and you want to do it efficiently. This is where a very useful C# keyword comes in: yield.

This statement allows us to return elements individually without loading the entire collection into memory. This technique is known as "lazy iteration" or "lazy loading" meaning elements are only generated when needed. Let’s dive in, devs! ✌🏼

Definition of yield

yield is a keyword used in C# within iterator methods, which return a sequence of elements one at a time. These methods don’t return the whole collection all at once; instead, they pause execution at each yield and resume when more elements are requested.

When we write a method with yield return, we’re creating a data sequence that we can iterate through without having to store all elements simultaneously. This is especially useful when working with large data sets or when we only need part of a list.

How to use it

The basic usage of yield involves two main commands:

  • yield return: Used to return a value within a loop or an iterator method.
  • yield break: Used to end the data sequence and signal that no more values will be provided.

Simple example: Generating even numbers

Let’s create an example where we generate even numbers within a range efficiently – that is, only generating them when they’re needed. This is where yield comes in handy.

Code with yield return:

public IEnumerable<int> EvenNumbers(int limit)
{
    for (int i = 0; i <= limit; i += 2)
    {
        yield return i; //Here I return the even number
    }
}

What's happening here?

IEnumerable<int>: This type tells us that the method will return a sequence of integers, but lazily – one at a time.

yield return i: Each time the method is called, an even number is returned, and execution "pauses" until the next number is requested. Once the loop finishes, the method stops returning numbers.

How would this look without yield?

Without yield, you’d need to create a List object to store the iteration results and then return it, like so:

public List<int> EvenNumbers(int limit)
{
    List<int> evenNumbers = new List<int>(); //Create a list to store the even numbers
    for (int i = 0; i <= limit; i += 2)
    {
        evenNumbers.Add(i); //Add each number to the list
    }
    return evenNumbers; //Return the entire list
}

See the difference now? 🐿️✌🏼 With yield, it’s much better.

Now, let’s look at how you’d use the method in a program:

foreach (var number in GenerateEvenNumbers(12))
{
    Console.WriteLine(number);  //Here the output is: 0, 2, 4, 6, 8, 10, 12
}

Each time the foreach loop advances, the GenerateEvenNumbers method returns the next even number in the sequence. As you can see, we aren’t storing all the numbers in a list – we just generate one when it’s needed.

Why should I use yield?

Memory Efficiency: With yield, elements are only generated when requested, which is much more efficient than storing all elements in memory if you only need part of the list.

Flow Control: Using yield makes code clearer and easier to maintain. Instead of building an entire list of numbers and returning it, you can simply return one value at a time and let the consumer decide what to do with those values.

Performance on Large Collections: When working with large collections (like thousands of elements), yield avoids the need to load all the elements into memory at once, which can be costly in terms of time and space.

What about yield break?

As I mentioned earlier, we can also use yield break to indicate that the sequence has ended. This is useful when we want to stop iteration before all elements are processed.

Example with yield break

Suppose you have a list of comments and want to filter through them until you find a comment with the word “end” indicating that the thread has finished. An optimized code using yield and yield break would look like this:

public static IEnumerable<string> FilterComments(List<string> comments, string keyWord)
{
    foreach (var comment in comments)
    {
        if (comment.Contains(keyWord)) 
        {
            yield break;  //Ends the iteration when the keyword is found
        }
        yield return comment;
    }
}

In this example, the method returns all words until it finds keyWord. As soon as it does, iteration stops thanks to yield break, and no more elements (in this case, comments) are returned.

If you replace yield break; with just break;, you’ll notice that functionally, nothing changes – the code still works. However, it’s not internally correct, and efficiency is lost. These details distinguish an average developer from an exceptional one, so take note and look into this interesting case. You’re here reading and learning from a specialized blog, you are a code rockstar 🙌

Conclusions 🐿️🙌

Using yield in C# allows us to create iterator methods that return elements one by one, efficiently and without the need to store the entire collection in memory.

With yield return, we can create data sequences that generate items only when needed, improving program performance. And with yield break, we can end those sequences whenever we want, giving us more control over data handling.

As you’ve noticed, yield is a powerful tool that can optimize memory usage and enhance the readability of your code, especially when working with large collections or sequences that don’t need to be fully loaded upfront.

I’m sure you enjoyed this post, so go ahead and share it – that helps a ton, rockstar 🥳

Cover photo credits: Photo from Alex Kotliarskyi on Unsplash

Leave a Reply

Your email address will not be published. Required fields are marked *