﻿# What is yield and how does it work in C\#?

C\# capabilities keep expanding from year to year\. New features enrich software development\. However, their advantages may not always be so obvious\. For example, the good old yield\. To some developers, especially beginners, it's like magic \- inexplicable, but intriguing\. This article shows how yield works and what this peculiar word hides\. Have fun reading\!

![0808_What_is_yield_and_how_does_it_work/image1.png](https://import.viva64.com/docx/blog/0808_What_is_yield_and_how_does_it_work/image1.png)

## Why you need yield

The _yield_ keyword is used to build generators of element sequences\. These generators do not create collections\. Instead, the sequence stores the current state \- and moves on to the next state on command\. Thus, memory requirements are minimal and do not depend on the number of elements\. It's not hard to guess that generated sequences can be infinite\.

In the simplest scenario, the generator stores the current element and contains a set of commands that must be executed to get a new element\. This is often much more convenient than creating a collection and storing all of its elements\.

While there is nothing wrong with writing a class to implement the generator's behavior, _yield_ simplifies creating such generators significantly\. You do not have to create new classes \- everything works already\.

I must point out here that _yield_ is not a feature available exclusively in C\#\. However, while the concept is the same, in different languages _yield_ may be implemented and used differently\. Which is why here's one more reminder that this article talks about _yield_ only in the context of C\#\.

## How to use yield

### A standard case

To begin, create a method that generates the sequence you need\. The only limitation here is that the method must return one of the following types:

* _IEnumerable_
* _IEnumerable<T\>_
* _IEnumerator_
* _IEnumerator<T\>_

Though you can use _yield_ in methods, properties and operators, to simplify this article I'll review only methods\.

Take a look at this simple _yield_ method:

```cpp
static IEnumerator<int> GetInts()
{
  Console.WriteLine("first");
  yield return 1;

  Console.WriteLine("second");
  yield return 2;
}

static void Main()
{
  IEnumerator<int> intsEnumerator = GetInts(); // print nothing
  Console.WriteLine("...");                    // print "..."

  intsEnumerator.MoveNext();                   // print "first"
  Console.WriteLine(intsEnumerator.Current);   // print 1
}
```

When the _GetInts_ function is called, it returns an object that implements _IEnumerator<int\>_\. Then the method exits before it can reach any other code\.

The _MoveNext_ method's first call executes the code inside _GetInts_ \- until the first _yield return_\. The value specified in the _yield return_ is assigned to the _Current_ property\.

Thus, this code's first output is "\.\.\.", then "first", and at the end "1" \- a value from the _Current_ property\.

The next time you call _MoveNext_ again, the method's execution will pick up where it left off\. The console will display the "second" message, and 2 will be recorded to the _Current_ property\.

Calling _MoveNext_ for the third time will start executing the _GetInts_ method from the moment it was earlier suspended\. Since the _GetInts_ method contains no more code, the third _MoveNext_ method call will return _false_\. Further _MoveNext_ method's calls will have no effect and will also return _false_\.

If you call the _GetInts_ method once more, it will return a new object that will allow you to start generating new elements\.

### Local variables, fields, and properties

Local variables initialized inside _yield_ methods, retain their values between _MoveNext_ method calls\. For example:

```cpp
IEnumerator<double> GetNumbers()
{
  string stringToPrint = "moveNext";
  Console.WriteLine(stringToPrint);  // print "moveNext"
  yield return 0;
  Console.WriteLine(stringToPrint);  // print "moveNext"
  stringToPrint = "anotherStr";
  yield return 1;
  Console.WriteLine(stringToPrint);  // print "anotherStr"
}
```

If you use the _GetNumbers_ method to create a new generator, the first two times you call the generator's _MoveNext_ method, the output will be "moveNext"\. The _MoveNext_ method's third call will print "anotherStr"\. This is predictable and logical\. 

However, working with fields and properties may not be as simple\. For example:

```cpp
string message = "message1";

IEnumerator<int> GetNumbers()
{
  Console.WriteLine(message);
  yield return 0;
  Console.WriteLine(message);
  yield return 1;
  Console.WriteLine(message);
}
void Method()
{
  var generator = GetNumbers();
  generator.MoveNext(); // print "message1"
  generator.MoveNext(); // print "message1"
  message = "message2";
  generator.MoveNext(); // print "message2"
}
```

In the code sample above, the _GetNumbers_ method accesses and uses the _message_ field\. The field value changes while the sequence is being generated \- and this change affects the sequence generation logic\.

A similar thing happens with properties: if a property value changes, this may affect the generated sequence\.

### yield break

Aside from _yield return_, C\# offers you another statement \- _yield break_\. It allows you to stop sequence generation \- that is, exit the generator for good\. If the _MoveNext_ method executes _yield break_, the return is _false_\. No changes to fields or properties can make the generator work again\. However, if the method that uses _yield_ is called for the second time \- it's a completely different story, because a new object generator is created\. That generator would not have encountered _yield break_\.

Let's take a look at a sample generator that uses _yield break_:

```cpp
IEnumerator<int> GenerateMultiplicationTable(int maxValue)
{
  for (int i = 2; i <= 10; i++)
  {
    for (int j = 2; j <= 10; j++)
    {
      int result = i * j;

      if (result > maxValue)
        yield break;

      yield return result;
    }
  }
}
```

The _GenerateMultiplicationTable_ method multiplies numbers from 2 to 10 by each other and returns a sequence that contains the results\. If the numbers' product exceeds a defined limit \(the _maxValue_ parameter\), the sequence generation stops\. This generator exhibits this behavior thanks to _yield break_\.

### Returning IEnumerable

As I mentioned at the beginning, a method that uses _yield_ can return _IEnumerable_, that is, a sequence itself instead of the sequence's iterator\. An _IEnumerable_ type object often proves to be more convenient, because the _IEnumerable_ interface provides many extension methods, and also supports the _foreach_ loop\. 

_Note\. _If a method's return type is _IEnumerable_, the returned object implements both _IEnumerable_ and _IEnumerator_\. However, it's a bad idea to cast an _IEnumerable_ type object to _IEnumerator_ :\)\. Why? I'll explain later when we get under the hood of this system\.

For now, let's take a look at this example:

```cpp
void PrintFibonacci()
{
  Console.WriteLine("Fibonacci numbers:");

  foreach (int number in GetFibonacci(5))
  {
    Console.WriteLine(number);
  }
}

IEnumerable<int> GetFibonacci(int maxValue)
{
  int previous = 0;
  int current = 1;

  while (current <= maxValue)
  {
    yield return current;

    int newCurrent = previous + current;
    previous = current;
    current = newCurrent;
  }
}
```

The _GetFibonacci_ method returns the Fibonacci sequence whose two first elements equal 1\. Since the method's return type is _IEnumerable_, the _PrintFibonacci_ method can use the _foreach_ loop to traverse the elements inside the sequence\. 

Note that each time _PrintFibonacci_ iterates through the _IEnumerable_ sequence, the _GetFibonacci_ function executes from the beginning\. Here's why this happens\. The _foreach_ loop uses the _GetEnumerator_ method to traverse elements inside the sequence\. Every new _GetEnumerator_ call returns an object that iterates through the sequence elements from the very beginning\. For example:

```cpp
int _rangeStart;
int _rangeEnd;

void TestIEnumerableYield()
{
  IEnumerable<int> polymorphRange = GetRange();

  _rangeStart = 0;
  _rangeEnd = 3;

  Console.WriteLine(string.Join(' ', polymorphRange)); // 0 1 2 3

  _rangeStart = 5;
  _rangeEnd = 7;

  Console.WriteLine(string.Join(' ', polymorphRange)); // 5 6 7
}

IEnumerable<int> GetRange()
{
  for (int i = _rangeStart; i <= _rangeEnd; i++)
  {
    yield return i;
  }
}
```

At the _string\.Join_ first call, the function iterates through the _IEnumerable_ type object for the first time, and as a result the _GetRange_ method is executed\. You could achieve a similar result by writing a _foreach_ loop\. Then the _\_rangeStart_ and _\_rangeEnd_ fields are set to new values and \- behold \- we get a different result from iterating through **the very same** _IEnumerable_ type object\! 

If you are familiar with LINQ, such behavior may not seem so unusual \- after all, the results of LINQ queries are processed the same way\. Less experienced developers, however, may be stumped by this phenomenon\. Remembering that in some scenarios _IEnumerable_ objects and LINQ queries deliver such results will save you a lot of time in the future\. 

Aside from repeated queries being able to produce unexpected results, there is another problem\. All operations done to initialize elements will be repeated\. This can have a negative effect on the application's performance\. 

## When do I use yield?

You can use _yield_ everywhere in your app or nowhere at all\. This depends on the particular case and particular project\. Aside from the obvious use cases, this construction can help you simulate parallel method execution\. The Unity game engine often employs this approach\.

As a rule, you do not need _yield_ for simple element filtering or to transform elements from an existing collection \- LINQ can handle this in most cases\. However, _yield_ allows you to generate sequences of elements that do not belong to any collection\. For example, when working with a tree, you may need a function that traverses a particular node's ancestors:

```cpp
public IEnumerable<SyntaxNode> EnumerateAncestors(SyntaxNode node)
{
  while (node != null)
  { 
    node = node.Parent;
    yield return node;
  }
}
```

The _EnumerateAncestors_ method allows you to traverse ancestors starting from the closest one\. You do not need to create collections, and you can stop element generation at any moment \- for example when the function finds a specific ancestor\. If you have ideas on how to implement this behavior without _yield_ \(and your code is at least somewhat concise\), I'm always looking forward to your comments below :\)\. 

## Limitations

Despite its many advantages and possible use cases, the _yield_ statement has a number of limitations related to its internal implementation\. I clarified some of them in the next section that explores how the _yield_ statement's magic works\. For now, let's just take a look at the list of those restrictions:

* although the _IEnumerator_ interface contains the _Reset_ method, _yield_ methods return objects that implement the _Reset_ method incorrectly\. If you try to call such object's _Reset_ method, the _NotSupportedException_ exception will be thrown\. Be careful with this: do not pass a generator object to methods that might call its _Reset_ method;
* you cannot use_ yield_ in anonymous methods or lambda\-expressions;
* you cannot use _yield_ in methods that contain unsafe code;
* you cannot use the _yield return_ statement inside the _try\-catch_ block\. However, this limitation does not apply to _try_ statements inside _try\-finally_ blocks\. You can use _yield break_ in _try_ statements inside both _try\-catch_ and _try\-finally_ blocks\.

## So how exactly does this work?

Let's use the dotPeek utility to see what _yield_ statements look like under the hood\. Below is the _GetFibonacci_ function that generates the Fibonacci sequence until the _maxValue_ limitation is reached:

```cpp
IEnumerable<int> GetFibonacci(int maxValue)
{
  int previous = 0;
  int current = 1;

  while (current <= maxValue)
  {
    yield return current;

    int newCurrent = previous + current;
    previous = current;
    current = newCurrent;
  }
}
```

Let's enable the 'Show compiler\-generated code' setting and decompile the application with dotPeek\. What does the _GetFibonacci_ method really look like?

Well, something like this:

```cpp
[IteratorStateMachine(typeof(Program.<GetFibonacci>d__1))]
private IEnumerable<int> GetFibonacci(int maxValue)
{
  <GetFibonacci>d__1 getFibonacciD1 = new <GetFibonacci>d__1(-2);
  getFibonacciD1.<>4__this = this;
  getFibonacciD1.<>3__maxValue = maxValue;
  return (IEnumerable<int>)getFibonacciD1;
}
```

Almost nothing like the original method, right? Not to mention that the code looks a little strange\. Well, let's take a crack at it\.

First, we'll translate the whole thing into a language we can understand \(no, not IL\):

```cpp
[IteratorStateMachine(typeof(GetFibonacci_generator))]
private IEnumerable<int> GetFibonacci(int maxValue)
{
  GetFibonacci_generator generator = new GetFibonacci_generator(-2);
  generator.forThis = this;
  generator.param_maxValue = maxValue;
  return generator;
}
```

This code is the same, but the names are easier on the eyes, and excessive code structures are eliminated\. Also, the C\# compiler has no problem understanding this code, in comparison to the code listed earlier\. This is the code format I use from now on in the article\. If you want to see what this code looks like as\-is, grab dotPeek \(or even better \- ildasm\) and go ahead :\)\. 

This code creates a special object\. The object stores a link to the current item and the _maxValue_ parameter value\. '\-2' is passed to the constructor \- as we see further, this is the generator's starting state\.

The compiler created the generator class automatically, and all the logic we put into the function is implemented there\. Now we can take a look at what this class contains\.

Let's start with the declaration:

```cpp
class GetFibonacci_generator : IEnumerable<int>,
                               IEnumerable,
                               IEnumerator<int>,
                               IEnumerator,
                               IDisposable
```

Nothing unexpected, really\.\.\. Except for _IDisposable_ that came out of nowhere\! It may also seem odd that the class implements _IEnumerator_, even though the _GetFibonacci_ method returns _IEnumerable<int\>_\. Let's figure out what happened\.

Here's the constructor:

```cpp
public GetFibonacci_generator(int startState)
{
  state = startState;
  initialThreadId = Environment.CurrentManagedThreadId;
}
```

The _state_ field stores the '\-2' _startState_ value passed to the generator at the initialization\. The _initialThreadId_ field stores the ID of the thread where the object was created\. I'll explain the purpose of these fields later\. Now let's take a look at the _GetEnumerator_ implementation:

```cpp
IEnumerator<int> IEnumerable<int>.GetEnumerator()
{
  GetFibonacci_generator generator;
  
  if (state == -2 && initialThreadId == Environment.CurrentManagedThreadId)
  {
    state = 0;
    generator = this;
  }
  else
  {
    generator = new GetFibonacci_generator(0);
    generator.forThis = forThis;
  }
  
  generator.local_maxValue = param_maxValue;
  
  return generator;
}
```

See how when certain conditions are met, the method returns the same object instead of a new one? This peculiarity might seem quite unexpected\. The following code fragment confirms it: 

```cpp
IEnumerable<int> enumerable = prog.GetFibonacci(5);
IEnumerator<int> enumerator = enumerable.GetEnumerator();

Console.WriteLine(enumerable == enumerator);
```

This code's output is 'True'\. Who would have thought? :\)

At the _GetEnumerator_ method call, the returned object's _state_ field is assigned to '0'\. This is an important step\.

After the conditional statement, another meaningful assignment takes place:

```cpp
generator.local_maxValue = param_maxValue
```

Take another look at the _GetFibonacci_ method \(or, to be exact, at what the compiler transformed it into\)\. See how the _maxValue_ parameter is recorded into the _param\_maxValue_ field? It is also recorded to the _local\_maxValue_ field\.

At first glance, it may seem unclear why the generator uses two fields \- _param\_maxValue_ and _local\_maxValue_ \- to store the _maxValue_ parameter\. I'll clarify the mechanics of this further on in this article\. Right now, let's take a look at the _MoveNext_ method:

```cpp
bool IEnumerator.MoveNext()
{
  switch (state)
  {
    case 0:
      state = -1;
      local_previous = 0;
      local_current = 1;
      break;
    case 1:
      state = -1;
      local_newCurrent = local_previous + local_current;
      local_previous = local_current;
      local_current = local_newCurrent;
      break;
    default:
      return false;
  }
  
  if (local_current > local_maxValue)
    return false;
  
  _current = local_current;
  state = 1;
  
  return true;
}
```

This method implements all logic we programmed into the _GetFibonacci_ method\. Before _MoveNext_ exits, it writes the current result into the _\_current_ field\. This is the value we get when we access the sequence generator's _Current_ property\.

If the sequence generation must be stopped \(in this case when _local\_current \> local\_maxValue_\), the generator's _state_ remains equal to '\-1'\. When the generator's _state_ field value is '\-1', the generator exits \- _MoveNext_ does not do anything and returns _false_\.

Note that when _MoveNext_ returns _false_, the _\_current_ field value \(as well as the _Current_ property value\) remains unchanged\.

### Tricks with type casting

Previously we discussed that when you create a new generator, the '\-2' value is recorded to the _state_ field\. But take a look at the code\. If _state_ _\= \-2_, then _MoveNext_ does not perform any actions and returns _false_\. Essentially, the generator does not work\. Luckily, the _GetEnumerator_ method call replaces the \-2 state with 0\. What about calling _MoveNext_ without calling _GetEnumerator_? Is this possible?

The _GetFibonacci_ method's return type is _IEnumerable_, thus, there is no access to the _MoveNext_ method\. Nevertheless, the returned object implements both _IEnumerable_ and _IEnumerator_ \- so you can use type casting\. In this case the developer does not need _GetEnumerator_ and can call the generator's _MoveNext_\. However, all calls will return _false_\. Thus, though you may be able to 'cheat' the system, this hardly benefits you in any way\.

_Conclusion_\. When a _yield_ method returns an _IEnumerable_ type object, this object implements both _IEnumerable_ and _IEnumerator_\. Casting this object to _IEnumerator_ produces a generator that is useless until the _GetEnumerator_ method is called\. At the same time, if a generator seems 'dead', it may suddenly start working after the _GetEnumerator_ method call\. The code below demonstrates this behavior:

```cpp
IEnumerable<int> enumerable = GetFibonacci(5);
IEnumerator<int> deadEnumerator = (IEnumerator<int>)enumerable;

for (int i = 0; i < 5; ++i)
{
  if (deadEnumerator.MoveNext())
  {
    Console.WriteLine(deadEnumerator.Current);
  }
  else
  {
    Console.WriteLine("Sorry, your enumerator is dead :(");
  }
}

IEnumerator<int> enumerator = enumerable.GetEnumerator();
Console.WriteLine(deadEnumerator == enumerator);

for (int i = 0; i < 5; ++i)
{
  if (deadEnumerator.MoveNext())
  {
    Console.WriteLine(deadEnumerator.Current);
  }
  else
  {
    Console.WriteLine("Sorry, your enumerator is dead :(");
  }
}
```

What do you think the console will display after the code above is executed? Hint: The code produces the Fibonacci sequence's first five elements \- 1, 1, 2, 3, 5\.

We have just reviewed a case of casting to _IEnumerator_\. Is it possible to play around with casting to _IEnumerable_?

Obviously, an object returned by _GetEnumerator_'s first call can be cast to _IEnumerable_ and will work as expected\. Take a look at this example:

```cpp
IEnumerable<int> enumerable = GetInts(0);                     
IEnumerator<int> firstEnumerator = enumerable.GetEnumerator();
IEnumerable<int> firstConverted = (IEnumerable<int>)firstEnumerator;

Console.WriteLine(enumerable == firstEnumerator);
Console.WriteLine(firstConverted == firstEnumerator);
Console.WriteLine(firstConverted == enumerable);
```

This code above prints three 'True' entries in the console window, because all three references point to the same object\. Here, casting does not bring any surprises, and will produce a link to an existing \(and, therefore, correctly working\) object\.

What about a different scenario? For example, _GetEnumerator_ is called for the second time or in a different thread \- and the value it returns is cast to _IEnumerable_\. Take a look at this sample _yield_ method:

```cpp
IEnumerable<string> RepeatLowerString(string someString)
{
  someString.ToLower();

  while (true)
  {
    yield return someString;
  }
}
```

At a first glance the _RepeatLowerString_ method receives a string as a parameter, converts it to lowercase and returns it indefinitely\. 

Have you noticed something odd in the code above? The _RepeatLowerString_ method, opposite to what you may expect, generates a sequence of references to the unchanged _someString_ string\.

This happens because the _ToLower_ method creates a new string and does not modify the original string\. It is not too important in our case, but in real software such mistakes lead to sad consequences and they are worth fighting against\. An incorrect _ToLower_ method call may not seem significant\. However, sometimes a function is called incorrectly somewhere in a large pile of code \- and that error is almost impossible to track down\.

If the project is large, its developers often use a static code analyzer\. A static code analyzer is an application that can quickly detect many code bugs\. For example, a static code analyzer could scan the _RepeatLowerString_ method and find that error I described earlier\. However, the analyzer is definitely not limited to detecting "meaningless calls" \- it covers an extensive list of problems\. 

I recommend that you use a static analyzer on your projects\. The PVS\-Studio tool is a good choice\. It checks projects written in C\#, C, C\+\+, and Java and detects a wide variety of problems in source code\. Interested? You can read more about PVS\-Studio on its official website and get the analyzer's free trial version\.

Meanwhile, I fixed the _RepeatLowerString_ method:

```cpp
IEnumerable<string> RepeatLowerString(string someString)
{
  string lower = someString.ToLower();

  while (true)
  {
    yield return lower;
  }
}
```

Now let's experiment with casting to _IEnumerable_:

```cpp
IEnumerable<string> enumerable = RepeatLowerString("MyString");
IEnumerator<string> firstEnumerator = enumerable.GetEnumerator();

IEnumerator<string> secondEnumerator = enumerable.GetEnumerator();
var secondConverted = (IEnumerable<string>)secondEnumerator;

var magicEnumerator = secondConverted.GetEnumerator();

for (int i = 0; i < 5; i++)
{
  magicEnumerator.MoveNext();
  Console.WriteLine(magicEnumerator.Current);
}
```

What will the console display after this code is executed?

![0808_What_is_yield_and_how_does_it_work/image2.png](https://import.viva64.com/docx/blog/0808_What_is_yield_and_how_does_it_work/image2.png)

Nothing\! All this masterful formation will crash with _NullReferenceException_\. Didn't expect this?

Maybe not\. Buy now we already have enough information to explain this behavior\. Let's walk through the example step\-by\-step\.

The exception was thrown when _magicEnumerator\.MoveNext\(\)_ called the _ToLower_ method\. _ToLower_ is called for the _someString_ parameter\. Inside the generator, this parameter is represented by two fields: _param\_someString_ and _local\_someString_:

```cpp
public string param_someString;
private string local_someString;
```

Note that the _MoveNext_ method \(where the exception was thrown\) uses the _local\_someString_ field:

```cpp
bool IEnumerator.MoveNext()
{
  switch (this.state)
  {
    case 0:
      this.state = -1;
      this.local_lower = this.local_someString.ToLower();
      break;
    case 1:
      this.state = -1;
      break;
    default:
      return false;
  }
  this._current = this.local_lower;
  this.state = 1;
  return true;
}
```

The _null_ value was recorded into the _local\_someString_ field\. But where did this value come from?

When _GetEnumerator_ is called, the value from _param\_someString_ is always written to the _local\_someString_ field of the returned object:

```cpp
IEnumerator<string> IEnumerable<string>.GetEnumerator()
{
  RepeatLowerString_generator generator;
  
  if (state == -2 && initialThreadId == Environment.CurrentManagedThreadId)
  {
    state = 0;
    generator = this;
  }
  else
  {
    generator = new RepeatLowerString_generator(0);
    generator.forThis = forThis;
  }
  
  generator.local_someString = param_someString;
  
  return generator;
}
```

Is that where _null_ came from? Yes it is\. But how did _null_ end up in this field? Let's take one more look at the code snippet:

```cpp
IEnumerable<string> enumerable = RepeatLowerString("MyString");
IEnumerator<string> firstEnumerator = enumerable.GetEnumerator();

IEnumerator<string> secondEnumerator = enumerable.GetEnumerator();
var secondConverted = (IEnumerable<string>)secondEnumerator;

var magicEnumerator = secondConverted.GetEnumerator();

for (int i = 0; i < 5; i++)
{
  magicEnumerator.MoveNext(); // NRE
  Console.WriteLine(magicEnumerator.Current);
}
```

The second time _GetEnumerator_ is called, we get a new object that has a correct value in the _local\_SomeString_ field\. Does the _GetEnumerator_ method also set the _param\_someString_ value? Sadly, no\. So this field gets the default value \- that is, that very _null_\. 

And then the _param\_someString_ field is used to set _local\_someString_ for the _magicEnumerator_ object\! And the exception is thrown exactly when the _MoveNext_ method attempts to call _local\_someString\.ToLower\(\)_\.

_Conclusion_\. If _GetEnumerator_ returns something other than _this_, the resulting object cannot fulfill the role of _IEnumerable_\. Such object's _param\_\*_ fields will not have values necessary for correct operation\. This peculiarity does not affect _yield_ methods that do not require any parameters\. For example:

```cpp
IEnumerable<int> GetPositive()
{
  int i = 0;
  
  while (true)
    yield return ++i;
}
```

The _GetPositive_ method returns an ascending sequence of positive numbers, starting with 1\. Now take a look at the _GetPositive_ method use example:

```cpp
IEnumerable<int> enumerable = GetPositive();
IEnumerator<int> firstEnumerator = enumerable.GetEnumerator();

IEnumerator<int> secondEnumerator = enumerable.GetEnumerator();
var secondConverted = (IEnumerable<int>)secondEnumerator;

IEnumerator<int> magicEnumerator = secondConverted.GetEnumerator();

for (int i = 0; i < 5; i++)
{
  magicEnumerator.MoveNext();
  Console.WriteLine(magicEnumerator.Current);
}
```

This code works correctly and displays numbers 1 through 5 on the screen\. But don't do this\. No, really :\)\.

### 2 fields for one parameter

When reviewing the generated class, you may have an inevitable question: why this class has two fields to store the parameter value \- instead of one\. By this time, you may have guessed what is happening here, but just in case, let's take a closer look\.

Here's another _yield_ method:

```cpp
IEnumerable<int> GetInts(int i)
{
  while (true)
  {
    yield return i++;
  }
}
```

This is a simple method that produces an ascending sequence of integers, starting with _i_ that is passed as a parameter\. The created generator's _MoveNext_ method looks something like this:

```cpp
bool IEnumerator.MoveNext()
{
  switch (this.state)
  {
    case 0:
      this.state = -1;
      break;
    case 1:
      this.state = -1;
      break;
    default:
      return false;
  }
  this._current = this.local_i++;
  this.state = 1;
  return true;
}
```

Look closely\. The important part is, the _local\_i_ field's value is incremented every time _MoveNext_ is called\. This field's initial value was set at the _GetEnumerator_ method's call\. The value is retrieved from the second field \- in this case, _param\_i_:

```cpp
IEnumerator<int> IEnumerable<int>.GetEnumerator()
{
  GetInts_generator generator;
  
  if (   state == -2 
      && initialThreadId == Environment.CurrentManagedThreadId)
  {
    state = 0;
    generator = this;
  }
  else
  {
    generator = new GetInts_generator(0);
    generator.forThis = forThis;
  }
  
  generator.local_i = param_i;
  
  return generator;
}
```

The _GetInts_ _yield_ method's call sets the _param\_i_ field's value:

```cpp
[IteratorStateMachine(typeof(GetInts_generator))]
private IEnumerable<int> GetInts(int i)
{
  GetInts_generator generator = new GetInts_generator(-2);
  generator.forThis = this;
  generator.param_i = i;
  return generator;
}
```

After this the _param\_i_ value never changes\. Why do we need the _param\_i_ field here? Why, for example, won't we assign a value straight to _local\_i_?

The _GetInts_ _yield_ method we listed earlier returns _IEnumerable_ type objects\. For this type of objects you can call _GetEnumerator_ several times\. As we know, at the first call the generator returns itself\. Keeping this thought in mind, let's take a look at the following code:

```cpp
IEnumerable<int> enumerable = GetInts(0);
// enumerable.param_i = 0

IEnumerator<int> firstEnumerator = enumerable.GetEnumerator(); 
// firstEnumerator.local_i = enumerable.param_i

Console.WriteLine(enumerable == firstEnumerator); // True

firstEnumerator.MoveNext(); 
// firstEnumerator.local_i++
firstEnumerator.MoveNext(); 
// firstEnumerator.local_i++

IEnumerator<int> secondEnumerator = enumerable.GetEnumerator(); 
// secondEnumerator.local_i = ?
```

In the first line, _GetInts_ is called, and it returns the _enumerable_ generator\. The '0' argument we passed to the _GetInts_ method is written to the generator's _param\_i_ field\. Then we get _firstEnumerator_\. This will be practically the same object as _enumerable_\. At the _GetEnumerator_ method's call, an _IEnumerator_ type object is returned\. This object's _local\_i_ field is assigned the value from the _enumerable_ object's _param\_i_ field\.

Then the _MoveNext_ method is called a couple of times\. This leads to changes in the _local\_i_ value \- both for _firstEnumerator_ and _enumerable_, because these links refer to the same object\.

At the end of the code snippet, the second _IEnumerator_ is acquired\. What do you think, is the value of the _local\_i_ field at initialization? Obviously, the value is the same as the one passed to the _GetInts_ _yield_ method initially\.

This is exactly the value that the _param\_i_ field stores\. No matter how the _local\_i_ value changes with _MoveNext_ calls, the _param\_i_ field remains unchanged\. As we saw earlier, the _param\_i_ field's value is recorded to the _local\_i_ field object the _GetEnumerator_ method returns\.

_Conclusion_\. Objects the _GetEnumerator_ method returns, are to an extent independent of each other\. To start generating sequences, they use parameters passed at the _yield_ method's call\. This is possible thanks to storing the original parameter in an additional field\.

### Returning an IEnumerator object

Above we reviewed a few features of generators, whose classes are based on _yield_ methods that return _IEnumerable_\. All of them are in some way connected to the fact that the generator class implements both _IEnumerator_ and _IEnumerable_\. Everything is much simpler with classes generated based on methods that return _IEnumerator_, because such generator classes do not implement _IEnumerable_\. Consequently, type casting tricks we discussed earlier will not work anymore\. Below I listed the main features of classes generated for the _yield_ method that returns _IEnumerator_ and the _yield_ method that returns _IEnumerable_:

* no _GetEnumerator_ method;
* no _initialThreadId_ field;
* the use of one field to store parameter values instead of two\.

Aside from this, there is a slight difference in how the generator classes are created\. You may remember when a generator class is created for the _yield_ method that returns _IEnumerable_, a '\-2' value is recorded to the _state_ field and the value is changed only when _GetEnumerator_ is called\. When _state_ is '\-2', the _MoveNext_ method does not do anything and returns _false_\. 

If a generator is created for a method that returns _IEnumerator_, it does not have any _GetEnumerator_ methods\. Which is why '0' is recorded to the _state_ field right after an item is instantiated\.

### Why the generator implements Dispose

The generator is forced to implement _Dispose_, because _IEnumerable<T\>_ derives from _IDisposable_\. In most cases the generator's _Dispose_ method is empty\. However, sometimes _Dispose_ contains code\. These cases involve the using operator\.

Take a look at the code fragments below:

```cpp
using (var disposableVar = CreateDisposableObject())
{
  ....
}
```



```cpp
using var disposableVar = CreateDisposableObject();
....
```

This code ensures the _Dispose_ method is called for a _disposableVar_ object \- either when the first block exits \(first example\), or when the method exits \(second example\)\. You can read more about _using_ in the [official documentation](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/using-statement)\.

The _using_ statement inside the _yield_ method affects the generator class the compiler creates\. In particular, _Dispose_ can be called for objects that are inside _using_ blocks\. However, _Dispose_ will be called even if an exception was thrown during execution \- this is the _using_ operator's expected behavior\.

As you might guess, the generator's Dispose method makes Dispose calls for all the corresponding fields\. Such fields represent local variables involved with the using operator inside the original yield method\.

Let's take a look at the example below:

```cpp
static IEnumerable<string> GetLines(string path)
{
  using (var reader = new StreamReader(path))
  {
    while (!reader.EndOfStream)
      yield return reader.ReadLine();
  }
}
```

This method returns an object that reads information from a file line by line\. The _using_ block does not affect the _GetEnumerator_ method contents, but leads to a new method emerging:

```cpp
private void Finally1()
{
  this.state = -1;
  if (this.local_reader == null)
    return;
  this.local_reader.Dispose();
}
```

After _Dispose_ is called, the _state_ field is assigned a value that will force _MoveNext_ to not perform any actions and return _false_\.

There may be more than one of such _finally_ methods\. If a yield method contains several _using_ blocks, more _finally_ methods are added and the structure of the _MoveNext_ and _Dispose_ methods become more complex\. Here's what the _Dispose_ method looks in this simple case:

```cpp
void IDisposable.Dispose()
{
  switch (this.state)
  {
    case -3:
    case 1:
      try
      {
      }
      finally
      {
        this.Finally1();
      }
      break;
  }
}
```

At first glance, the structure looks unnecessarily complicated\. However, making the original method's structure more complex and including several _using_ statements fill the method with meaning\. If this sounds interesting to you, I suggest you experiment with this yourself :\)\.

Calling the generator's _Dispose_ method makes sense if you need to stop sequence generation and free used resources\. There may be other cases when this call and inheritance from _IDisposable_ is handy\. If you have ideas about what these scenarios may be, please share them in the comments below\.

Now let's take a quick look at _MoveNext_:

```cpp
bool IEnumerator.MoveNext()
{
  try
  {
    switch (this.state)
    {
      case 0:
        this.state = -1;
        this.local_reader = new StreamReader(this.local_path);
        this.state = -3;
        break;
      case 1:
        this.state = -3;
        break;
      default:
        return false;
    }
    if (!this.local_reader.EndOfStream)
    {
      this._current = this.local_reader.ReadLine();
      this.state = 1;
      return true;
    }
    this.Finally1();
    this.local_reader = null;
    return false;
  }
  fault
  {
    Dispose();
  }
}
```

This code executes when you've included the _using_ operator into the _yield_ method\. Take a look at the _fault_ block\. In fact, at the time I am writing this article C\# does not support this type of structure\. However, this structure is used in IL\-code\. Here's how it works in the simplest case: if an exception is thrown in the _try_ block, the steps from the _fault_ block are performed\. Although, I suppose, everything is not that simple here\. What do you think? Please share your thoughts about the _fault_ block features in the comments below :\)\.

Thus, you can be sure that _Dispose_ is called for all variables declared through _using_, and exactly when needed\. Errors do not affect this behavior\.

### Do not call Reset\!

Finally, let's make sure that the _Reset_ method in the generator class really does throw an exception\.

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

It's all clear here \- we can see _NotSupportedException_\. Consequently, you need to remember, that you should pass the generator only to methods that do not call _Reset_\. You can also pass the generator to methods that handle this exception correctly\.

## Conclusion

In this article I tried to gather information on _yield_ in C\# and to break it down for you into as many chunks as possible\. I examined various cases: from the simplest samples \- to methods with loops and branches\. I inspected cases when _yield_ is convenient and when there's no need for it\. I even 'looked under the hood', deepening your understanding of the code and helping you understand its magic\.

The 'Limitations' section mentioned that you cannot use _yield return_ inside _try\-catch_ blocks\. Now that you know what _yield_ methods really are, you can ponder upon this and other limitations\. If you want someone else to do it, you can click [here](https://blogs.msdn.microsoft.com/ericlippert/2009/07/16/iterator-blocks-part-three-why-no-yield-in-finally/) and [here](https://docs.microsoft.com/en-us/archive/blogs/ericlippert/iterator-blocks-part-four-why-no-yield-in-catch)\.

Methods that use _yield_ can really simplify your life sometimes\. Behind this magic exists an entire class the compiler generated, which is why I recommend you use the yield feature only when it is significantly more convenient that, for example, LINQ\. It is also important to differentiate between the cases, when 'lazy execution' is handy \- and when it's better to just stick elements into a good old _List_ and not worry :\)\.

If you liked my article, subscribe to [my Twitter account](https://twitter.com/Nikita30005701)\. Every once in a while, I write about fascinating features I find when coding \- or announce useful articles on various topics\.

Well, that's it for today\. Thank you for reading\!