The new yields statement sounds very convenient. In the past, one had to write a significant amount of code to implement the IEnumerator interface and expose an enumerator. That included considerations of concurrency, a loop variable bound to the instance or other methods to maintain current loop value during enumeration.
Fret no more, a new syntax is in town - the yields statement.
With the yields statement, IEnumerator implementation folds down to a scan one liner:
public class MyCollection : IEnumerable |
You can also provide an enumerator returning static values or “hard coded” number of values:
public class VerySimple : IEnumerable |
So that sounds great! No pesky Reset()
, MoveNext()
etc., no private index to hold on to, and even options to do more fancy things, like exposing only some of your items to enumeration:
public class Person |
That was easy, and pretty useful too. You get to have an easy syntax for emitting each value, and you get exact control over which item is exposed without implementing a whole sub class just for the enumeration.
Looking at this keyword and the simplicity of exposing enumerator one might be tempted to think there is some magic new framework for enumerating a collection with hooks and generic loops or something. To find out, I looked at the IL generated for the MyCollection class we just created.
As expected, we find the class has a method named GetEnumerator()
. It’s implementation is seemingly simple, instantiate some cryptically named class and return it.
public IEnumerator GetEnumerator() |
When you look at the implementation of the enumerator class itself, you get quite a few lines of code:
private sealed class <GetEnumerator>d__0 : IEnumerator<object>, IEnumerator, IDisposable |
So what is really going on here is that when you type out yield return x;
the compiler transforms this into a method stub, implants your loop logic in the MoveNext()
method of a new shiny enumerator class, and provides the standard requisite functions of IEnumerable
interface which support the foreach statement.
Is this good or bad? Certainly it serves well in many instances. For most of your daily uses for an enumerator this should work quite well. It’s strongly typed to the list item and uses your class’s values referenced directly.
What can be sub optimal about this? Multithreaded applications need to implement locking at the class level. Some collections in .NET implement an internal version number such that if the collection chances during enumeration an exception gets thrown to the enumerating thread. Not so here. If you want that behavior you’d have to implement it yourself.
You should note that the loop itself and any of your conditions get transformed by the compiler. The transformation, I trust, is functionally equivalent. The transformation result will vary slightly based on the collection being iterated, or if you are using a static chain of yield statements. In the case of hard coded yielded values, no concurrency issues should arise, but that is fairly rare in my humble experience.
Besides that, I think it’s pretty cool. You get to write less code, the compiler take care of your code generation.
On a side note, when decompiling your code, don’t get too caught up in Reflector’s code rendering. For one, IL decompiled to your language of choice is not a symmetric operation. For that reason and due to compiler optimizations and inlining, certain language constructs may come up reflected as GOTO label but were not necessarily coded this way originally in the higher level language.