13,253,507 members (60,754 online)
alternative version

#### Stats

30.4K views
21 bookmarked
Posted 7 Apr 2013

# Pick Your Enumerator & Me.Understand Yield and IEnumerable (C#)

, 21 Apr 2013
 Rate this:
Using multiple enumerators and implementing IEnumerable with Yield or IEnumerator.

## Introduction

I am developing an unfinished linear algebra (matrix) class and wanted to provide the ability to enumerate it horizontally (by row) or vertically (by column). So I tried to implement `IEnumerable`, this requires implementing a method called `GetEnumerator()`. Then it can get a little confusing.

• Creating a second class that implements `IEnumerator` and returning a new instance of it from the `GetEnumerator()` method isn't that hard
• Creating more than one isn't any more difficult (vertical and horizontal) and choosing between them using a property is straightforward
• But what about `yield`, how exactly do you use it? Is is 'better'? Do I use it in the matrix class or the `IEnumerator` class?
• Can you use `yield` and still have more than one way of enumerating?

I tend to find that articles on this subject (and many others) start simple enough and then get more complex / theoretical / detailed than I would like. I would prefer basic, practical advice and guidance with a smattering of theory and explanation, I can learn bit by bit, not all at once. I am not Sheldon. In fact I wasn't completely sure until I'd finished writing the attached demo app, then I understood it (on a practical basis). So, we will look at implementing `IEnumerable`, by creating `IEnumerator` classes or by the use of `yield` - and we will compare the two approaches. I will also mention at least one other way of making a class enumerable (without explicitly implementing `IEnumerable`). We will also see two ways of allowing a user of a class to choose from `n` different ways of enumerating that class (as many as you implement), one with `yield` and one with `IEnumerators`.

If you are simply looking to understand (as I was) if `yield` is an alternative to creating `IEnumerator` classes (rather than something complementary for example) then the answer is: yes; although there are differences which are probably not trivial in many cases, we'll have a short look at this.

### What is Yield?

If you arrived here trying to work out what `yield` is / does then here's my take.

When you use an `Iterator` method with `yield` it doesn't execute to its natural conclusion, every time it encounters a `yield` it does the same as a `return` statement and then stops right where it is, the method doesn't finish, then next time you call the method, it picks up where it left off, i.e. it does not start afresh each time you call it. So during a `foreach` loop these methods will run from start to finish once only, not once for each value in a collection, array or other grouping of data. It's a method that returns values many times not once - magic (or perhaps not magic, just the compiler saving us time since behind the scenes(^) it apparently creates `IEnumerator` like code anyway...)

### Who is it for?

1. Beginners
2. Anyone who hasn't implemented `IEnumerable`, (via an `IEnumerator` or `yield`) before and wants an example
3. Anyone who wants to implement more than one enumerator for an `IEnumerable` class
4. Anyone who hasn't managed to get their head around one of the million other explanations out there; this is just my addition to that collection and I hope you find it more digestable than the others you've read so far, if not then no shakes, you have 999,999 other options!

### Quick Summary

1. We can use `yield` as an alternative to creating an `IEnumerator` class whilst implementing `IEnumerable` (and specifically the `GetEnumerator()` method)
2. In some situations, particularly at moderate to high levels of complexity, `yield` can become orders of magnitude easier to code and understand / read than an `IEnumerator` class
3. A class that is `IEnumerable` can have more than one enumerator, user selectable at run time
4. You can make a class enumerable without declaring it `IEnumerable` but by implementing a method with a type of `IEnumerable`
1. Implementing more than one method that has an `IEnumerable` return type has the same effect as #3
5. When you use `yield`, the compiler creates an `IEnumerator` class, so in some ways it's no different, but the code you write may well be easier to read (efficiency of the code is a different question), see the last section

Warning 1: If you like your patterns and principles then even in code this short I have no doubt I've broken many. C'est la vie!

Warning 2: If there is a lack of detail in the semantics, then you have my apologies. I am writing from a practical perspective and sometimes to keep it simple, practical and readable I intentionally (or unintentionally) skip over the detail. For this reason the way two different people interpret the missing detail might be different. If you need or want to get that detail absolutely correct then there is masses of such information out there and I'm not going to try and replicate it here, that's not the intention. If there are clear written errors then I'm very happy to correct them.

## Background

The genesis of this article was (is) an unfinished linear alegbra (matrix) project. I wanted to provide the ability to traverse the matrix by column (vertically) or by row (horizontally) which meant implementing `IEnumerable`. OK, so how do we do that then? Well, we start with the classic Google search. A great deal of somewhat frustrating time later I had a good idea I could use the `yield` keyword or I could create a class that implemented `IEnumerator`, or maybe I had to do both, could do both, didn't have to do either or had to do something else entirely; who knew?

Ultimately you can use `yield` or create a class that implements `IEnumerator`, your choice. There may be things that each is better suited for, I don't know enough yet to say one way or the other. As for my matrix class I can also implement 2 enumerators (or 10 if I really wanted), each doing something different and which can be interchanged at run time.

## The Basics of Enumerating

The 'classic' (or 'old' if you prefer new shiny things) way of implementing `IEnumerable` is fairly straight forward so I'll start with a verbal description and then add the code .

The `IEnumerable` interface simply demands you implement a `GetEnumerator()` method that returns an object that has implemented `IEnumerator`. Then `IEnumerator` demands you implement several methods, the two key ones are the `MoveNext()` method and the `Current` property.

So imagine you use a `foreach` loop on a object that has implemented `IEnumerable`, such as this:

```foreach (double element in EnumerableMatrix)
{
Console.WriteLine(element.ToString());
}```

What's actually happening is that the `GetEnumerator()` method is called on the `EnumerableMatrix` object. This returns a new object which we know must have implemented `IEnumerator`. This `IEnumerator` object can then be looped over using the `MoveNext()` method and the `Current` property of `IEnumerator`. In fact the code above is shorthand for this.

```IEnumerator ObjectToEnumerate = EnumerableMatrix.GetEnumerator()
while (while ObjectToEnumerate.MoveNext() == True)
{
Console.WriteLine(ObjectToEnumerate.Current.ToString());
}```

You could do this manually in order to gain more control over the enumeration than provided by `foreach`. In the Points of Interest section we have a quick look at the IL code and we can see directly that `foreach` is turned into `MoveNext()` and `Current` as shown above.

## To Understand Enumeration we Must First Count to Ten

### Implements IEnumerable

So, asuming we understand what a `foreach` loop actually does then it gets even easier to understand the 'old' or classic way of making an object enumerable. We start with the class we want to enumerate.

```public class ByEnumerator : IEnumerable<double>
{
Public IEnumerator<Double> GetEnumerator()
{
Return New MyNewEnumerator()
}

System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}  ```

It really is that straightforward. `IEnumerable` simply demands implementation of those two methods and you just point the less specific one at the other.

In my case I was enumerating a two dimensional array of double and wanted to be able to choose between two different `IEnumerators`. So we need a few changes. We've done the following:

Added a constructor that simply takes an existing 2 dimensional array of double.

```public ByEnumerator(double[,] matrix)
{
this._matrix = matrix;
this._matrixEnumerator = MatrixEnumerator.Horizontal;
}```

A property to allow the choice between different enumeration methods and an `enum` to represent the different possible enumerators.

```public MatrixEnumerator Enumerator
{
get { return this._matrixEnumerator; }
set { this._matrixEnumerator = value; }
}

public enum MatrixEnumerator
{
Vertical,
Horizontal
}```

And finally some conditional code to create the desired `IEnumerator` instance.

```public IEnumerator<double> GetEnumerator()
{
switch (this._matrixEnumerator)
{
case MatrixEnumerator.Vertical:
return new VerticalMatrixEnumerator(this._matrix);
case MatrixEnumerator.Horizontal:
return new HorizontalMatrixEnumerator(this._matrix);
default:
throw new InvalidOperationException();
}
}

System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}```

Now all we need to do is implement those two new classes, the Horizontal and Vertical enumerators.

### Implements IEnumerator

You can see that the `IEnumerator` we return depends on the class property, `Enumerator`. Now we just need to create two two `IEnumerator` classes mentioned in the code above. We'll show the Horizontal one first and you'll see that we simply add code to the four methods mentioned earlier. There are various private fields to keep track of where we are in the array and other than that the important method is `MoveNext()`. You can also see that we pass a copy of the 2D array from the `IEnumerable` class to the `IEnumerator` class (I assume this is why modifiying an object whilst enumerating it tends to cause chaos).

First off the private fields and the constructor.

```public class HorizontalMatrixEnumerator : IEnumerator<double>
{
private double[,] _matrix;
private int _colIndex;
private int _rowIndex;
private double _curItem;
private int _lastCol;
private int _lastRow;

public HorizontalMatrixEnumerator(double[,] matrix)
{
this._matrix = matrix;
this._colIndex = -1;
this._rowIndex = 0;
this._curItem = double.NaN;
this._lastCol = matrix.GetUpperBound(1);
this._lastRow = matrix.GetUpperBound(0);
}```
• The `_matrix` field is pretty obvious, it's a copy of the 2d array of double sent by the `IEnumerator` object (I am deliberately avoiding discussion of shallow / deep copies and values / references - keep the focus)
• The `_colIndex` and `_rowIndex` fields keep track of where we are in the array, our position. We'll explain why they are initialised as `0` and `-1` in a minute.
• `_curItem` is the field behind the `Current` property.
• `_lastCol` and `_lastRow` just provide convenient access to the `UpperBounds` of the array.

Now the `Current` property.

```public double Current
{
get
{
if (double.IsNaN(this._curItem))
{
throw new InvalidOperationException();
}
return this._curItem;
}
}

object System.Collections.IEnumerator.Current
{
get { return this.Current(); }
}
```

As noted, it's just a wrapper for the `_curItem` field. It throws an exception if it's `NaN` - it is initialised as such in the constructor - this ensures that we throw an exception if the user of the object (the `foreach` loop for example) tries to use it before `MoveNext()` has been called, it's a bit nicer than letting them find out when their code throws a wobbly because it was expecting a useful value and got something it didn't know how to deal with.

Now the interesting bit, `MoveNext()`.

```public bool MoveNext()
{
if (this._colIndex == this._lastCol & this._rowIndex == this._lastRow)
{
return false;
}
if (this._colIndex == this._lastCol)
{
this._colIndex = 0;
this._rowIndex += 1;
}
else
{
this._colIndex += 1;
}
this._curItem = this._matrix[this._rowIndex, this._colIndex];
return true;
}
```
• First off we check we are not already at the end of the array, if we are then we've already finished and we return `false`, allowing the `while {true} do ...` loop from earlier to exit gracefully
• Then we check if we are at the last column in a row, if we are we reset the column index to `0` and move to the next row
• If we weren't at the end of a row then we simply move to the next column in that row
• In the latter two cases we return `true`, telling the `while {true} do ...` loop that there's at least one more new value to loop over
• Now the explanation I promised earlier, why do we initialise `_rowIndex` as `0` and `_colIndex` as `-1`? Well, `MoveNext()` gets called before `Current`, so if we set both to `0` then the code above would immedaitely move us to the second column in the first row (0,1) and we would never set the value of `Current` to be the array value at (0,0)

It's worth noting that the `MoveNext()` method presented above is essentially a pair of nested `for` loops which have been made unnecessarily complex, the `yield` version (that we will show shortly) is easier to code and is also easier to understand. The image below should make that clear.

You can see that every time the column index reaches `2` we need to reset it to `0` and add `1` to the row index, or simply add `1` to the column index until is does reach `2`.

Finally the `Reset()` method just puts the relevant fields back to how they were when the object was created.

```    public void Reset()
{
this._colIndex = -1;
this._rowIndex = 0;
this._curItem = double.NaN;
}
} // Class```

The Vertical enumerator changes a few things but mostly the `MoveNext()` method as shown below. We just flip around the row and column variables in the `if () ... else ...` section such that we move down columns rather than across rows. We also initialise `_rowIndex` to `-1` (instead of `0`) and `_colIndex` to `0` (instead of `-1`).

```public bool MoveNext()
{
if (this._colIndex == this._lastCol & this._rowIndex == this._lastRow)
{
return false;
}
if (this._rowIndex == this._lastRow)
{
this._rowIndex = 0;
this._colIndex += 1;
}
else
{
this._rowIndex += 1;
}
this._curItem = this._matrix[this._rowIndex, this._colIndex];
return true;
}
```

Now whilst that's pretty easy, it means creating two extra classes and (relatively) more code than feels necessary for something relatively simple, can we do better? Let's have a crack at yield...

## Who Needs IEnumerators?

### Yield to temptation. It may not pass your way again

So how do we do the same thing with `yield`? Most of the code doesn't change. The first thing is that the `GetEnumerator()` method doesn't create new objects, it calls a couple of private class methods. Other than that it's identical, you have to be watching carefully just to notice that something has changed.

```public IEnumerator<double> GetEnumerator()
{
switch (this._matrixEnumerator)
{
case MatrixEnumerator.Horizontal:
return this.HorizontalEnumerator();
case MatrixEnumerator.Vertical:
return this.VerticalEnumerator();
default:
throw new InvalidOperationException();
}
}```

Essentially identical, but rather than `return new HorizontalMatrixEnumerator(this._matrix)` we see `return this.HorizontalEnumerator()`, which is a method reference not an object. Then we just need to implement those two new methods.

```private IEnumerator<double> VerticalEnumerator()
{
if (this._matrix != null)
{
for (int col = 0; col <= this._matrix.GetUpperBound(1); col++)
{
for (int row = 0; row <= this._matrix.GetUpperBound(0); row++)
{
yield return this._matrix[row, col];
}
}
} else {
throw new InvalidOperationException();
}
}

private IEnumerator<double> HorizontalEnumerator()
{
if (this._matrix != null)
{
for (int row = 0; row <= this._matrix.GetUpperBound(0); row++)
{
for (int col = 0; col <= this._matrix.GetUpperBound(1); col++)
{
yield return this._matrix[row, col];
}
}
} else {
throw new InvalidOperationException();
}
}```

It took me a while to figure out exactly how to declare those two methods but eventually you work out that they need to have a return type of `IEnumerator`. You can see this in the code and other than that it involves no more than looping your way through the array. Not much easier technically, in fact no easier at all but certainly less code and without those extra classes to maintain.

What is very clear though is that it's a lot less code than two `IEnumerator` classes. It's also a lot easier to see what's going on, it is clearly two nested `for` loops, which as we said above is what the `MoveNext()` method is actually doing, only in a more complex way.

By way of explanation this really isn't very much, but if you've read and understood how to do the same thing with `IEnumerators` then understanding `yield` is as simple as this:

1. Recognising that the code looks really very similar to the `MoveNext()` methods (in essence), only it's simpler
2. Realising that when you use an `Iterator` method with `yield` it doesn't execute to its natural conclusion, every time it encounters a `yield` it does the same as a `return` statement and then stops right where it is, the method doesn't finish, then next time you call the method it picks up where it left off, i.e. it does not start afresh each time you call it. So during a `foreach` loop this method will run from start to finish once only, not once for each value in the array. It's a method that returns values many times not once - magic (or perhaps not magic, just the compiler saving us time since behind the scenes(^) it apparently creates `IEnumerator` like code anyway...)

The only difference between the two is which way you loop through the array, columns first or rows first.

### State Machines

If this explanation doesn't work for you there's of options out there that explain it in different ways and to greater detail, Google(^) is your friend.

OK, I'm not going to try and explain this in great detail (the article is aimed at beginners and there's much I don't yet understand or at least I imagine there is), just enough (I hope) to make sense of what `yield` actually does and how. You can almost think of the `Iterator` method as a separate programme, the programme running the `foreach` loop on your `IEnumerable` object then sends a message to the other one saying 'give me the next one' - right now this sounds exactly like a `MoveNext()` followed by a `Current` - but the clever bit is the other programme stops execution as soon as it has executed `yield` statement, then waits until you ask it for the next one, at which point it executes the next statement and continues until it reachs the next `yield` statement, executes it and then waits once more. In the case above each time it reaches the 'next' `yield` statement it is actually the same one, but in a loop, you could equally have a set of `yield` statements one after the other (see the MSDN example(^))

## Points of Interest

### Did you learn anything interesting / fun / annoying?

Yes, some donkey (when designing .Net and VB / C#) decided that it would be smart to index arrays as (row, column) and DataGridViews as (column, row).

### Did you do anything particularly clever or wild or zany?

Wild or zany? This is what the CP article template suggests. Really? When coding? Wild and zany. Maybe if I drank too much coffee and did something really crazy that was in no way related to coding but otherwise, no.

### foreach == while (MoveNext()) + Current

We can show clearly the point made earlier, that running foreach on an `IEnumerable` object is the same as manually creating the `IEnumerator` and initiating a `while (true) do ...` loop. This happens to be the code for the `foreach` performed on the object which uses an `IEnumerator` rather than `yield`, although it's very similar indeed for the `yield` version.

```IL_016d: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<float64> IEnumerableCS.ByEnumerator::GetEnumerator()
IL_0172: stloc.s CS\$5\$0001
.try
{
IL_0174: br.s IL_01a1
IL_0176: ldloc.s CS\$5\$0001
IL_0178: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1<float64>::get_Current()
IL_017d: conv.r8
IL_017e: stloc.s Val
IL_0180: nop
IL_0181: ldloc.s output
IL_0183: ldloc.s Val
IL_0185: ldc.i4.2
IL_0186: call float64 [mscorlib]System.Math::Round(float64, int32)
IL_018b: stloc.s CS\$0\$0002
IL_018d: ldloca.s CS\$0\$0002
IL_018f: call instance string [mscorlib]System.Double::ToString()
IL_0194: ldstr " ¦ "
IL_0199: call string [mscorlib]System.String::Concat(string, string, string)
IL_019e: stloc.s output
IL_01a0: nop

IL_01a1: ldloc.s CS\$5\$0001
IL_01a3: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_01a8: stloc.s CS\$4\$0000
IL_01aa: ldloc.s CS\$4\$0000
IL_01ac: brtrue.s IL_0176
// end loop

IL_01ae: leave.s IL_01c4
} // end .try
finally
{
IL_01b0: ldloc.s CS\$5\$0001
IL_01b2: ldnull
IL_01b3: ceq
IL_01b5: stloc.s CS\$4\$0000
IL_01b7: ldloc.s CS\$4\$0000
IL_01b9: brtrue.s IL_01c3

IL_01bb: ldloc.s CS\$5\$0001
IL_01bd: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_01c2: nop

IL_01c3: endfinally
// end handler```

You can clearly see that at `IL_0174` it jumps (unconditionally) to the short section that includes the call to `MoveNext()`, starting at `IL_01a1`. At the end of that section it branches on `true` (`IL_01b9`) to the slightly longer section above (assuming `MoveNext()` returned `true`), starting at `IL_0176` which then calls `Current`. Completely consistent with a `while (true) do ...` loop, i.e. if the object to enumerate is empty it never tries to call `Current`; meanwhile for a non-empty object the first time that `MoveNext()` returns `false` is when it tries to move past the last position, at which point the branch on `true` (`IL_01b9`) doesn't branch and the code flows onwards out of the loop.

### How Does the Compiler Implement Yield?

I thought I'd remembered reading that when you use yield the compiler responds by creating an `IEnumerator`, so it's really just shorthand to allow for code that's easier to read, write and maintain, I thought I'd see if this was true - since I have the perfect examnple to test it on - by looking at what IL code is produced by the compiler. (Try ILSpy(^) or the ILDisassembler that comes with Windows 7.1 SDK(^).) Here's what you see, first we'll look at the classes and code that is produced, in particular the ByYield class.

There's our `HorizontalEnumerator` method highlighted, now what does the IL code say?

```.method private hidebysig
instance class [mscorlib]System.Collections.Generic.IEnumerator`1<float64> HorizontalEnumerator () cil managed
{
// Method begins at RVA 0x22ec
// Code size 16 (0x10)
.maxstack 2
.locals init (
[0] class IEnumerableCS.ByYield/'<HorizontalEnumerator>d__4'
)

IL_0000: ldc.i4.0
IL_0001: newobj instance void IEnumerableCS.ByYield/'<HorizontalEnumerator>d__4'::.ctor(int32)
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldarg.0
IL_0009: stfld class IEnumerableCS.ByYield IEnumerableCS.ByYield/'<HorizontalEnumerator>d__4'::'<>4__this'
IL_000e: ldloc.0
IL_000f: ret
} // end of method ByYield::HorizontalEnumerator```

I don't get all of that, but I do see a reference to `IEnumerableCS.ByYield/'<HorizontalEnumerator>d__4`. We can see that in the image just above - it's the third item in the `ByYield` description, just under `DerivedTypes`. Here's what's contained in that class member.

There's are old friends, two methods `MoveNext()` and `Reset()`, and the `Current` property; those methods were not written in the `ByYield` class, the compiler has created them. Cearly the compiler treats `yield` as shorthand for 'please write me an `IEnumerator`'.

We can compare, at a very amateur level, whether the compilers version of an `IEnumerator` is much better than its attempt to optimise the `MoveNext()` we wrote above. The compilers conversion of the two nested `for` loops with a `yield` in the middle is on the left. It's conversion of my `MoveNext()` method is on the right.

```.method private final hidebysig newslot virtual
instance bool MoveNext () cil managed
{
.override method instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
// Method begins at RVA 0x225c
// Code size 248 (0xf8)
.maxstack 4
.locals init (
[0] bool CS\$1\$0000,
[1] int32 CS\$4\$0001,
[2] bool CS\$4\$0002
)

IL_0000: ldarg.0
IL_0001: ldfld int32 IEnumerableCS.ByYield/'<HorizontalEnumerator>d__4'::'<>1__state'
IL_0006: stloc.1
IL_0007: ldloc.1
IL_0008: switch (IL_0019, IL_0017)

IL_0015: br.s IL_001b

IL_0017: br.s IL_007f

IL_0019: br.s IL_0020

IL_001b: br IL_00f2

IL_0020: ldarg.0
IL_0021: ldc.i4.m1
IL_0022: stfld int32 IEnumerableCS.ByYield/'<HorizontalEnumerator>d__4'::'<>1__state'
IL_0027: nop
IL_0028: ldarg.0
IL_0029: ldfld class IEnumerableCS.ByYield IEnumerableCS.ByYield/'<HorizontalEnumerator>d__4'::'<>4__this'
IL_002e: ldfld float64[0..., 0...] IEnumerableCS.ByYield::_matrix
IL_0033: ldnull
IL_0034: ceq
IL_0036: stloc.2
IL_0037: ldloc.2
IL_0038: brtrue IL_00ea

IL_003d: nop
IL_003e: ldarg.0
IL_003f: ldc.i4.0
IL_0040: stfld int32 IEnumerableCS.ByYield/'<HorizontalEnumerator>d__4'::'<row>5__5'
IL_0045: br.s IL_00c4

IL_0047: nop
IL_0048: ldarg.0
IL_0049: ldc.i4.0
IL_004a: stfld int32 IEnumerableCS.ByYield/'<HorizontalEnumerator>d__4'::'<col>5__6'
IL_004f: br.s IL_0095

IL_0051: nop
IL_0052: ldarg.0
IL_0053: ldarg.0
IL_0054: ldfld class IEnumerableCS.ByYield IEnumerableCS.ByYield/'<HorizontalEnumerator>d__4'::'<>4__this'
IL_0059: ldfld float64[0..., 0...] IEnumerableCS.ByYield::_matrix
IL_005e: ldarg.0
IL_005f: ldfld int32 IEnumerableCS.ByYield/'<HorizontalEnumerator>d__4'::'<row>5__5'
IL_0064: ldarg.0
IL_0065: ldfld int32 IEnumerableCS.ByYield/'<HorizontalEnumerator>d__4'::'<col>5__6'
IL_006a: call instance float64 float64[0..., 0...]::Get(int32, int32)
IL_006f: stfld float64 IEnumerableCS.ByYield/'<HorizontalEnumerator>d__4'::'<>2__current'
IL_0074: ldarg.0
IL_0075: ldc.i4.1
IL_0076: stfld int32 IEnumerableCS.ByYield/'<HorizontalEnumerator>d__4'::'<>1__state'
IL_007b: ldc.i4.1
IL_007c: stloc.0
IL_007d: br.s IL_00f6

IL_007f: ldarg.0
IL_0080: ldc.i4.m1
IL_0081: stfld int32 IEnumerableCS.ByYield/'<HorizontalEnumerator>d__4'::'<>1__state'
IL_0086: nop
IL_0087: ldarg.0
IL_0088: dup
IL_0089: ldfld int32 IEnumerableCS.ByYield/'<HorizontalEnumerator>d__4'::'<col>5__6'
IL_008e: ldc.i4.1
IL_0090: stfld int32 IEnumerableCS.ByYield/'<HorizontalEnumerator>d__4'::'<col>5__6'

IL_0095: ldarg.0
IL_0096: ldfld int32 IEnumerableCS.ByYield/'<HorizontalEnumerator>d__4'::'<col>5__6'
IL_009b: ldarg.0
IL_009c: ldfld class IEnumerableCS.ByYield IEnumerableCS.ByYield/'<HorizontalEnumerator>d__4'::'<>4__this'
IL_00a1: ldfld float64[0..., 0...] IEnumerableCS.ByYield::_matrix
IL_00a6: ldc.i4.1
IL_00a7: callvirt instance int32 [mscorlib]System.Array::GetUpperBound(int32)
IL_00ac: cgt
IL_00ae: ldc.i4.0
IL_00af: ceq
IL_00b1: stloc.2
IL_00b2: ldloc.2
IL_00b3: brtrue.s IL_0051

IL_00b5: nop
IL_00b6: ldarg.0
IL_00b7: dup
IL_00b8: ldfld int32 IEnumerableCS.ByYield/'<HorizontalEnumerator>d__4'::'<row>5__5'
IL_00bd: ldc.i4.1
IL_00bf: stfld int32 IEnumerableCS.ByYield/'<HorizontalEnumerator>d__4'::'<row>5__5'

IL_00c4: ldarg.0
IL_00c5: ldfld int32 IEnumerableCS.ByYield/'<HorizontalEnumerator>d__4'::'<row>5__5'
IL_00ca: ldarg.0
IL_00cb: ldfld class IEnumerableCS.ByYield IEnumerableCS.ByYield/'<HorizontalEnumerator>d__4'::'<>4__this'
IL_00d0: ldfld float64[0..., 0...] IEnumerableCS.ByYield::_matrix
IL_00d5: ldc.i4.0
IL_00d6: callvirt instance int32 [mscorlib]System.Array::GetUpperBound(int32)
IL_00db: cgt
IL_00dd: ldc.i4.0
IL_00de: ceq
IL_00e0: stloc.2
IL_00e1: ldloc.2
IL_00e2: brtrue IL_0047

IL_00e7: nop
IL_00e8: br.s IL_00f1

IL_00ea: nop
IL_00eb: newobj instance void [mscorlib]System.InvalidOperationException::.ctor()
IL_00f0: throw

IL_00f1: nop

IL_00f2: ldc.i4.0
IL_00f3: stloc.0
IL_00f4: br.s IL_00f6

IL_00f6: ldloc.0
IL_00f7: ret
} // end of method '<HorizontalEnumerator>d__4'::MoveNext```
```.method public final hidebysig newslot virtual
instance bool MoveNext () cil managed
{
// Method begins at RVA 0x251c
// Code size 139 (0x8b)
.maxstack 4
.locals init (
[0] bool CS\$1\$0000,
[1] bool CS\$4\$0001
)

IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldfld int32 IEnumerableCS.HorizontalMatrixEnumerator::_colIndex
IL_0007: ldarg.0
IL_0008: ldfld int32 IEnumerableCS.HorizontalMatrixEnumerator::_lastCol
IL_000d: ceq
IL_000f: ldarg.0
IL_0010: ldfld int32 IEnumerableCS.HorizontalMatrixEnumerator::_rowIndex
IL_0015: ldarg.0
IL_0016: ldfld int32 IEnumerableCS.HorizontalMatrixEnumerator::_lastRow
IL_001b: ceq
IL_001d: and
IL_001e: ldc.i4.0
IL_001f: ceq
IL_0021: stloc.1
IL_0022: ldloc.1
IL_0023: brtrue.s IL_002a

IL_0025: nop
IL_0026: ldc.i4.0
IL_0027: stloc.0
IL_0028: br.s IL_0089

IL_002a: ldarg.0
IL_002b: ldfld int32 IEnumerableCS.HorizontalMatrixEnumerator::_colIndex
IL_0030: ldarg.0
IL_0031: ldfld int32 IEnumerableCS.HorizontalMatrixEnumerator::_lastCol
IL_0036: ceq
IL_0038: ldc.i4.0
IL_0039: ceq
IL_003b: stloc.1
IL_003c: ldloc.1
IL_003d: brtrue.s IL_0058

IL_003f: nop
IL_0040: ldarg.0
IL_0041: ldc.i4.0
IL_0042: stfld int32 IEnumerableCS.HorizontalMatrixEnumerator::_colIndex
IL_0047: ldarg.0
IL_0048: dup
IL_0049: ldfld int32 IEnumerableCS.HorizontalMatrixEnumerator::_rowIndex
IL_004e: ldc.i4.1
IL_0050: stfld int32 IEnumerableCS.HorizontalMatrixEnumerator::_rowIndex
IL_0055: nop
IL_0056: br.s IL_0068

IL_0058: nop
IL_0059: ldarg.0
IL_005a: dup
IL_005b: ldfld int32 IEnumerableCS.HorizontalMatrixEnumerator::_colIndex
IL_0060: ldc.i4.1
IL_0062: stfld int32 IEnumerableCS.HorizontalMatrixEnumerator::_colIndex
IL_0067: nop

IL_0068: ldarg.0
IL_0069: ldarg.0
IL_006a: ldfld float64[0..., 0...] IEnumerableCS.HorizontalMatrixEnumerator::_matrix
IL_006f: ldarg.0
IL_0070: ldfld int32 IEnumerableCS.HorizontalMatrixEnumerator::_rowIndex
IL_0075: ldarg.0
IL_0076: ldfld int32 IEnumerableCS.HorizontalMatrixEnumerator::_colIndex
IL_007b: call instance float64 float64[0..., 0...]::Get(int32, int32)
IL_0080: stfld float64 IEnumerableCS.HorizontalMatrixEnumerator::_curItem
IL_0085: ldc.i4.1
IL_0086: stloc.0
IL_0087: br.s IL_0089

IL_0089: ldloc.0
IL_008a: ret
} // end of method HorizontalMatrixEnumerator::MoveNext```

I'm not qualified to comment with any professionalism, but whilst the compiler clearly has to do more work with `yield` in order to turn two nested `for` loops into an `IEnumerator` than when it is given dedicated `IEnumerator` code, there isn't (seemingly) a huge difference between the two. Whether one of them is more efficient I don't know - maybe someone has time to test... I guess it shouldn't be a surprise, the fact that the compiler is capable of doing this at all impresses me, especially if you imagine just how complex some methods with a `yield` statement might be and it has to be able to handle tham all.

It's probably worth thinking about this is if you have a class that may contain very large amounts of data, which needs to be enumerable and where time is critical, in such cases trying both your own `IEnumerator` and `yield` is probably worth the time, to see which results in quicker execution.

## History

Version 5 (21 April 2013): Fixed issue with test for `var == double.NaN`, should have been `double.IsNaN(var)`; added section on 'foreach == while (MoveNext()) + Current' further typographical errors fixed and re-wrote some sections for clarity

Version 4 (13 April 2013): [No code changes in demo app] Re-wrote introduction to improve flow, fixed some typographical errors

Version 3 (12 April 2013): [No code changes in demo app] Changed do while to while do

Version 2 (11 April 2013): [No code changes in demo app] Clarifications and extra information subsequent to reader comments and addition of new section, 'How Does the Compiler Implement Yield?'

Version 1 (7 April 2013): Shiny and Newny and New

## Share

 Engineer France
A hobbyist begin-again-er!

Spent a few years longer ago than I care to remember with BBC Basic, a couple of years with Delphi about 10-15 years ago with a smattering af MS Access applications along the way. Dropped out of it completely except for the occasional Excel macro.

Finally picked up the baton again with VB.Net in VS2010 and now VS 2012and have been playing around quite a bit with a few odds and sodds, learning much as I go - and long may it continue.

I don't work even close to the IT industry and probably never will, but I do enjoy it as a hobby.

## You may also be interested in...

 Pro

 First Prev Next
 My vote of 5 Jaime Olivares15-Apr-13 6:24 Jaime Olivares 15-Apr-13 6:24
 A thing that is simple wrong. Paulo Zemek13-Apr-13 4:30 Paulo Zemek 13-Apr-13 4:30
 Re: A thing that is simple wrong. Andreas Gieriet14-Apr-13 4:34 Andreas Gieriet 14-Apr-13 4:34
 My vote of 4 Paulo Zemek13-Apr-13 4:25 Paulo Zemek 13-Apr-13 4:25
 My vote of 4 Shubham Choudhary11-Apr-13 21:13 Shubham Choudhary 11-Apr-13 21:13
 Nice article jfriedman11-Apr-13 17:36 jfriedman 11-Apr-13 17:36
 Re: Nice article jfriedman11-Apr-13 21:22 jfriedman 11-Apr-13 21:22
 Re: Nice article jfriedman12-Apr-13 15:36 jfriedman 12-Apr-13 15:36
 Re: Nice article Andreas Gieriet14-Apr-13 6:06 Andreas Gieriet 14-Apr-13 6:06
 Re: Nice article jfriedman14-Apr-13 6:47 jfriedman 14-Apr-13 6:47
 Re: Nice article Andreas Gieriet14-Apr-13 8:34 Andreas Gieriet 14-Apr-13 8:34
 Re: Nice article jfriedman14-Apr-13 8:58 jfriedman 14-Apr-13 8:58
 Re: Nice article jfriedman15-Apr-13 11:22 jfriedman 15-Apr-13 11:22
 Weird introduction, misleading descriptions, sloppy editing Andreas Gieriet11-Apr-13 14:08 Andreas Gieriet 11-Apr-13 14:08
 Re: Weird introduction, misleading descriptions, sloppy editing Andreas Gieriet12-Apr-13 1:00 Andreas Gieriet 12-Apr-13 1:00
 Re: Weird introduction, misleading descriptions, sloppy editing Andreas Gieriet14-Apr-13 4:21 Andreas Gieriet 14-Apr-13 4:21
 Re: Weird introduction, misleading descriptions, sloppy editing Andreas Gieriet15-Apr-13 7:18 Andreas Gieriet 15-Apr-13 7:18
 Re: Weird introduction, misleading descriptions, sloppy editing Andreas Gieriet15-Apr-13 10:09 Andreas Gieriet 15-Apr-13 10:09
 My vote of 3 Paulo Zemek8-Apr-13 4:48 Paulo Zemek 8-Apr-13 4:48
 Re: My vote of 3 Paulo Zemek8-Apr-13 11:12 Paulo Zemek 8-Apr-13 11:12
 Re: My vote of 3 Paulo Zemek8-Apr-13 13:50 Paulo Zemek 8-Apr-13 13:50
 Re: My vote of 3 Paulo Zemek9-Apr-13 4:15 Paulo Zemek 9-Apr-13 4:15
 Re: My vote of 3 Paulo Zemek8-Apr-13 14:17 Paulo Zemek 8-Apr-13 14:17
 Re: My vote of 3 Paulo Zemek9-Apr-13 4:25 Paulo Zemek 9-Apr-13 4:25
 Re: My vote of 3 Paulo Zemek9-Apr-13 7:57 Paulo Zemek 9-Apr-13 7:57
 1 - I should say that it is best to show how `yield return` can be used in methods that return `IEnumerator` and in methods that return `IEnumerable` (and yes, the compiler creates all the classes that are needed... showing at least the generated classes, without entering in IL details could be good). 2 - Completely agree. 3 - In fact I don't know if it implements reset or not... I really think it does not... but I never used it anyway. But accessing the same value many times is like calling `enumerator.Current` many times, without calling next... in such case, there is no difference. 4 - `MoveNext()` is not a pair of nested for loops... in the case of two nested fors it was doing the same job in another way. 5 - This is a little confusing. You will not make the class enumerable. You will make a method enumerable. A class can have many different enumerable methods, but the class itself will still not be enumerable. That is: you can't do a `foreach` on instances of that class, but you can do on the result of their methods. A big difference here is that in such situation you don't consider the class to be a collection, but you admit that their methods (or properties) are returning collections. For example: A `Type` is not a collection, but the `GetProperties()` and `GetMethods()` (and some other methods) return collections (ok, full arrays, but arrays are collections too).
 Re: My vote of 3 Paulo Zemek9-Apr-13 9:20 Paulo Zemek 9-Apr-13 9:20
 My vote of 4 Naz_Firdouse8-Apr-13 2:10 Naz_Firdouse 8-Apr-13 2:10
 My vote of 5 cjb1108-Apr-13 1:33 cjb110 8-Apr-13 1:33
 Last Visit: 31-Dec-99 19:00     Last Update: 21-Nov-17 6:06 Refresh 1

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Web02 | 2.8.171114.1 | Last Updated 21 Apr 2013