DEV Community

Renan Martins
Renan Martins

Posted on

C#’s Hidden Gem: How Yield Return Makes Iterators Simple

If you have ever tried to write an iterator manually, you know it can be painful. You need to manage indexes, track conditions, and build temporary lists.
This article continues my series From Blank to LINQ Mastery, exploring one of the simplest and most powerful features in C#, yield return.

What Are Iterators

An iterator is a method or object that lets you go through a sequence one element at a time.
In C#, it is any method that returns IEnumerable<T>.
The foreach loop uses this mechanism internally to iterate over data.

The Old Way (Without yield return)

public static IEnumerable<int> GetEvenNumbers(List<int> numbers)
{
    var result = new List<int>();
    foreach (var n in numbers)
    {
        if (n % 2 == 0)
            result.Add(n);
    }
    return result;
}
Enter fullscreen mode Exit fullscreen mode

This code works, but creates a new list in memory before returning the result.

The Modern Way (With yield return)

public static IEnumerable<int> GetEvenNumbers(List<int> numbers)
{
    foreach (var n in numbers)
    {
        if (n % 2 == 0)
            yield return n;
    }
}
Enter fullscreen mode Exit fullscreen mode

With yield return, C# automatically creates an iterator behind the scenes. Although, it's important to understand that this method behaves a little differently from what you might expect.
It does not run from start to finish in one call.
Instead, it pauses each time it returns a value and resumes from that exact point when the next element is requested.

Here is a quick example that makes this clear:

public static IEnumerable<int> SampleIterator()
{
    Console.WriteLine("Start");
    yield return 1;

    Console.WriteLine("Middle");
    yield return 2;

    Console.WriteLine("End");
}
Enter fullscreen mode Exit fullscreen mode

Now we have the method returning and iteratos, let's call it to see what happens when you iterate over it:

var sequence = SampleIterator();
Console.WriteLine("Before iteration");

foreach (var n in sequence)
    Console.WriteLine($"Value: {n}");

Console.WriteLine("After iteration");
Enter fullscreen mode Exit fullscreen mode

Let's check the output:

Before iteration
Start
Value: 1
Middle
Value: 2
End
After iteration
Enter fullscreen mode Exit fullscreen mode

Notice that function is not executed in its call. The method stops and resumes multiple times. Execution moves step by step, each time MoveNext() is called in the background.
This is known as deferred execution, and it is the key to how LINQ operators process data lazily and efficiently.

Adding Control with yield break

Sometimes you may want to stop the iteration early.
That is where yield break shows up.

public static IEnumerable<int> GetFirstThreeEvenNumbers(List<int> numbers)
{
    int count = 0;
    foreach (var n in numbers)
    {
        if (n % 2 == 0)
        {
            yield return n;
            count++;
        }

        if (count == 3)
            yield break;
    }
}
Enter fullscreen mode Exit fullscreen mode

This stops the sequence once three even numbers have been returned.
No extra memory or complex logic required.

⚠️ Tip: Be Careful with Database Iteration

When using yield return with data that comes from a database, remember that the database connection must remain open while the iterator is being read.
If you close or dispose the connection before finishing the iteration, you will get exceptions or incomplete results.
A good practice is to materialize the results (for example, with .ToList()) before the connection is closed, unless you have full control over when and how the data is consumed.

Why It Matters for LINQ

Most LINQ methods such as .Where(), .Select(), and .Take() use yield return internally.
That is how LINQ can process data efficiently, creating values only when they are actually needed.

Key Takeaways

  • yield return simplifies iteration logic.
  • It supports deferred execution, generating elements on demand.
  • It is the foundation for how LINQ operators work behind the scenes.
  • Be careful when combining it with database queries that depend on open connections.

✅ That’s all, folks!

💬 Let’s Connect

Have any questions, suggestions for improvement, or just want to share your thoughts?

Feel free to leave a comment here, or get in touch with me directly on LinkedIn — I’d love to connect!

Top comments (0)