In this article, we are going to learn how the yield break statement works in C#.

To download the source code for this article, you can visit our GitHub repository.

So let’s get going.

Iterator Methods in C#

Before discussing yield break, we must understand the concept of iterator methods in C#.

An iterator method defines how to generate elements in the sequence and return them one by one. When we place a yield statement inside a method, the compiler treats that method as an iterator method, also known as the iterator block.  An iterator method can return different types like IEnumerable, IEnumerator, IEnumerable<T>, or IEnumerator<T>. It uses the yield return statement to return elements, one by one.

From a compiler’s perspective, it treats an iterator method differently from a regular method, which we are going to see in this article. That said, we have already explained how to use the yield return statement to iterate over a collection and return the elements.  So in this article, we are going to focus on the functionality of the yield break statement.

Don't like the ads? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!

What Does yield break Do in C#?

Once we define an iterator method using the yield return statement, we will need a way to stop the iteration at some point. For instance, when some condition is met, or after we perform a specified number of iterations, etc. We can use the yield break statement for this purpose.  It will end the iteration and exit the iterator block.

That said, let’s see an iterator block in action so that we can understand this better.

First, let’s create a method to generate random years between 1900 and 2023:

public static class Utility
{
    private static readonly Random random = new();

    public static IEnumerable<int> GenerateRandomYears()
    {
        int year;
        while (true)
        {
            year = random.Next(1900, 2023);
            yield return year;
        }
    }
}

This method will keep on generating random years between 1900 and 2023 and return those. However, do not run this program yet as this is an infinite loop and will probably cause the system to crash.

So let’s add a break condition to stop the iteration once we encounter a leap year: 

public static IEnumerable<int> GenerateRandomYears()
{
    int year;
    while (true)
    {
        year = random.Next(1900, 2023);                            
        yield return year;
                
        if (year % 4 == 0)
        {
            Console.WriteLine($"{Environment.NewLine}Encountered Leap Year:{year}");
            yield break;                    
        }
    }            
}

Here we use a yield break statement to end the iterator block once we encounter a leap year.

Now, in the Main() method, let’s iterate through this iterator and print the years:

foreach (int year in Utility.GenerateRandomYears())
{
    Console.WriteLine(year);
}

Let’s run the application and observe the results:

1937
1986
1942
1918
1953
1989
1989
1920

Encountered Leap Year:1920

As expected, this program produces a list of random years between 1900 and 2023 and ends the iteration once it encounters a leap year. We have seen how we can use a yield break statement to end an iteration.

How Is a yield break Different From a break Statement?

Now a lot of us might have this question in mind – Can we just use the break statement to end an iteration instead of the yield break statement? Well the answer is yes we can, but both these statements behave differently. We should be aware of the differences to use them in the correct context.

When the compiler encounters a break statement inside a loop, it terminates the loop and executes the next statement after the loop. It means that if we have other statements after the loop, the compiler will still execute those before exiting the method.

On the other hand, when the compiler encounters a yield break statement inside an iterator block, it terminates the iterator block altogether and exits the method. It means that even if there are some additional statements after the loop, the compiler will not execute those. The compiler will even raise a warning that it has detected some unreachable code in this case.  We can think of the yield break statement inside an iterator method as something similar to a return statement inside a regular method that does not return anything.

An Example of yield break Statement Inside Iterator Method

For verifying this behavior, let’s add a statement after the loop to print some information:

public static IEnumerable<int> GenerateRandomYears()
{
    int year;
    while (true)
    {
        year = random.Next(1900, 2023);
        yield return year;

        if (year % 4 == 0)
        {
            Console.WriteLine($"{Environment.NewLine}Encountered Leap Year:{year}");
            yield break;
        }
    }

    Console.WriteLine("GenerateRandomYears() method executed successfully.");
}

We can see that compiler throws a warning that the last statement is unreachable.

Let’s run the application anyway and observe the output:

2014
2013
1936

Encountered Leap Year:1936

As expected, we can see that it does not execute the last statement. So a yield break statement exits the iterator without executing any further statements. 

An Example of break Statement Inside Iterator Method

Now let’s replace the yield break statement with a break statement:

public static IEnumerable<int> GenerateRandomYears()
{
    int year;
    while (true)
    {
        year = random.Next(1900, 2023);
        yield return year;

        if (year % 4 == 0)
        {
            Console.WriteLine($"{Environment.NewLine}Encountered Leap Year:{year}");
            break;
        }
    }

    Console.WriteLine("GenerateRandomYears() method executed successfully.");
}

After that, let’s run the program once again to see the output:

1902
1967
1958
1905
1969
1995
1920

Encountered Leap Year:1920
GenerateRandomYears() method executed successfully.

This time we can see that it executes the print statement after the loop. 

Thus, even though we can use both break and yield break statements inside an iterator method, both behave differently and we should choose them carefully based on what we want to do.

How Is a yield break Different From a return Statement?

Another question that a lot of people may have in mind is – Can we use just use the return statement to exit the iterator instead of using the yield break statement?

Well, a return statement terminates the execution of a regular method in which it appears and returns the control to the caller. While doing so, it can optionally return a value along with it. 

Now let’s try to replace the yield break statement inside the iterator with a return statement:

public static IEnumerable<int> GenerateRandomYears()
{
    int year;
    while (true)
    {
        year = random.Next(1900, 2023);
        yield return year;

        if (year % 4 == 0)
        {
            Console.WriteLine($"{Environment.NewLine}Encountered Leap Year:{year}");
            return;
        }
    }

    Console.WriteLine("GenerateRandomYears() method executed successfully.");
}

We can see that it returns a compiler error. So if we try to write a return statement for an iterator method, it will throw this error: 

compiler error CS1622 - Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration.

As obvious from the error, the iterator methods are different from the regular methods and do not support regular return statements. It can only use yield return and yield break statements.

This is a very important difference between iterator methods and regular methods. An iterator method can have “yield return” statements to return values one by one and yield break statements to exit the iterator. But we cannot use a regular return statement in an iterator method.

Conclusion

In this article, we learned the usage of yield break statements for iterators in C#. We learned how iterator methods are different from regular methods.  Also, we learned how a yield break behaves differently from a break statement when we use it in iterator blocks. 

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!