How the C# yield keyword works
Tags: csharp, yield, lowering, roslyn
Published: 2019-05-20 15:20:37

---
title: How the C# yield keyword works
published: true
canonical_url: https://mazeez.dev/posts/csharp-yield
description: Have you ever wondered what is happening under the hood when you use the yield keyword?
tags: csharp, yield, lowering, roslyn
---

The [C# yield keyword](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/yield) is an interesting keyword, because when you use it, you can return an `IEnumerable<T>` without specifying any concrete implementation explicitly.

Here is a code snippet:

```csharp
static IEnumerable<int> GetNumbers()
{
    yield return 0;
    yield return 1;
    yield return 2;
}

static void Main(string[] args)
{
    var numbers = GetNumbers();
    foreach (var n in numbers)
        Console.WriteLine(n);
}
```

This is the output:

```
0
1
2
```

But how does this work? What does `GetNumbers` return exactly? Lets dig a little bit deeper:

```csharp
static void Main(string[] args)
{
    var numbers = GetNumbers();
    Console.WriteLine(numbers.GetType().Name);
}
```
`GetType()` returns the type of an object and `Name` gives us the name of the type.

We will see that it outputs `<GetNumbers>d__0`. Strange right? Because in C# you can't use `<` or `>` in identifiers. That's because `<GetNumbers>d__0` is actually a class that's generated by the compiler at compile time. It represents a [state machine](https://medium.freecodecamp.org/state-machines-basics-of-computer-science-d42855debc66) that implements `GetNumbers()`.

To understand it better, you have to see the decompiled C#, which is quite easy with the help of [SharpLab](https://sharplab.io/#v2:D4AQDABCCMDcCwAocVoBYGKQOwIYFsBTAZwAdcBjQiACUIBt6B7AdSYCd6ATJAbyQiCoAJlQB2AUP6Ihs1ADZUAZgA8AS2wAXAHwQA4oU0A5AK74ARoXbEAFAEpJciNKdOY0KGIhhMrue88IOEc/VA8QL2FfVwBfJBDZGEUQNAgAWVwNGxgwAG0AXQhcdgBzYgcZVxdQwQA3YohsM0trCABefUNTCytbO2iamABOGyae6wA6A00AFQBPUkJ7CaMCQn6EoTjKwW2YoA==):

```csharp
namespace HelloWorld
{
    internal class Program
    {
        [CompilerGenerated]
        private sealed class <GetNumbers>d__0 : IEnumerable<int>, IEnumerable, IEnumerator<int>, IDisposable, IEnumerator
        {
            private int <>1__state;

            private int <>2__current;

            private int <>l__initialThreadId;

            int IEnumerator<int>.Current
            {
                [DebuggerHidden]
                get
                {
                    return <>2__current;
                }
            }

            object IEnumerator.Current
            {
                [DebuggerHidden]
                get
                {
                    return <>2__current;
                }
            }

            [DebuggerHidden]
            public <GetNumbers>d__0(int <>1__state)
            {
                this.<>1__state = <>1__state;
                <>l__initialThreadId = Environment.CurrentManagedThreadId;
            }

            [DebuggerHidden]
            void IDisposable.Dispose()
            {
            }

            private bool MoveNext()
            {
                switch (<>1__state)
                {
                    default:
                        return false;
                    case 0:
                        <>1__state = -1;
                        <>2__current = 0;
                        <>1__state = 1;
                        return true;
                    case 1:
                        <>1__state = -1;
                        <>2__current = 1;
                        <>1__state = 2;
                        return true;
                    case 2:
                        <>1__state = -1;
                        <>2__current = 2;
                        <>1__state = 3;
                        return true;
                    case 3:
                        <>1__state = -1;
                        return false;
                }
            }

            bool IEnumerator.MoveNext()
            {
                //ILSpy generated this explicit interface implementation from .override directive in MoveNext
                return this.MoveNext();
            }

            [DebuggerHidden]
            void IEnumerator.Reset()
            {
                throw new NotSupportedException();
            }

            [DebuggerHidden]
            IEnumerator<int> IEnumerable<int>.GetEnumerator()
            {
                if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId)
                {
                    <>1__state = 0;
                    return this;
                }
                return new <GetNumbers>d__0(0);
            }

            [DebuggerHidden]
            IEnumerator IEnumerable.GetEnumerator()
            {
                return System.Collections.Generic.IEnumerable<System.Int32>.GetEnumerator();
            }
        }

        [IteratorStateMachine(typeof(<GetNumbers>d__0))]
        private static IEnumerable<int> GetNumbers()
        {
            return new <GetNumbers>d__0(-2);
        }

        private static void Main(string[] args)
        {
            Console.WriteLine(GetNumbers().GetType().Name);
        }
    }
}
```

As you can see, there is a hidden `<GetNumbers>d__0` class generated by the compiler and `GetNumbers()` is modified to return a new instance of that class. The most interesting part of `<GetNumbers>d__0` is the `MoveNext()` method in which the compiler translates the logic in GetNumbers() into a state machine.

[Roslyn](https://github.com/dotnet/roslyn) (the C# compiler) generates a lot of code on your behalf, this operation is called lowering. Some other examples are when you use a [`for each`](https://sharplab.io/#v2:D4AQDABCCMDcCwAocVoDoAyBLAdgRwWUhgFZCkcBDAWwFMBnAB0oGNaIAJWgG24HsA6nwBO3ACZIA3kgiyoAJlQB2GXOmI5m1ADYoAFggBZSrgAUMMAG0AuhErCA5vQCUqrRHXv3AN3sQcAK7UAEa0wvQQALwQAKKBdMKUwdy0aABKlDgOtKbQADQQ0GDOhF5aAGYitKwAFqa+wv4QuP5BoeGuGmVqbt0QAJAwAJymOCVyvWUAvpNyM12y81NAA=) or when you use [`async/await`](https://sharplab.io/#v2:D4AQDABCCMDcCwAocVoFYGKQOwIYFsBTAZwAdcBjQiACUIBt6B7AdSYCd6ATJAbyQiCoAJlQB2AUP6Ihs1ADYoADiiKAIkwDKTIgBcAFgEtsAcwCCxAJ7YKACgCUkuRGnOIASHcwAnLYBEAEKEAGYchH72mG5yIN6qAHRqDLiWtmhgYJFO0ai+fmbBuoTsEVFuAL5I2bIwiiAALBAAsrjGtjBgANoAuhC47CbEjjJurjmCGtp6RqYW1nb28QDihLpmAO6tRewOy6sASiQArvS6DmXOlSOCV+VAA=).

Knowing how the compiler translates your code helps you to understand the code better and it also helps you in troubleshooting. For example, if we have a piece of code like this:

```csharp
static IEnumerable<int> GetNumbers()
{
    yield return 0;
    throw new Exception();
}

static void Main(string[] args)
{
    try
    {
        var numbers = GetNumbers();
        foreach(var n in numbers)
        {
            Console.WriteLine(n);
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex);
    }
}

```

This will be the output:
```
0
System.Exception: Exception of type 'System.Exception' was thrown.
   at HelloWorld.Program.<GetNumbers>d__0.MoveNext()
   at HelloWorld.Program.Main(String[] args)
```

Notice how the first element is returned and then an exception is thrown in `<GetNumbers>d__0.MoveNext()`. Knowing what is `<GetNumbers>d__0` makes you more comforatable dealing with these kinds of exceptions.