Thursday 18 March 2010

Anonymous Methods and Iterator Blocks

Today, while doing a fast test with the Enumerable.SelectMany method (I'd used it mainly for flattening some IEnumerable<IEnumerable<T>>, and got a bit confused the other day when reading the signatures and realizing that's just a specific case of a broader scope) one "brilliant" idea came to my mind, use an anonymous method (a lambda statement) with an iterator block inside. A low priority background thread in my mind started to say "are you sure that can be done, schedule some processing time for me to think it better", but a higher priority thread was already hitting the keyboard.

Write some beautiful code like this:



   1:          IEnumerable<string> citiesList = cities.SelectMany(pair =>

   2:          {

   3:              yield return pair.MainCity;

   4:              yield return pair.SecondaryCity;

   5:          });



invoke the compiler from the command line (yes, sometimes I like it "old school") and let this error message wake you up from your aesthetic dreams...

The yield statement cannot be used inside an anonymous method or lambda expression

Do some googling and find this post from Eric Lippert explaining it. Worth reading material.

What I conclude from this is:
We sometimes forget all the logic behind the compiler magic. Anonymous methods mean the compiler has to create named methods in the background, or classes when these Anonymous methods grab variables from its lexical scope (closures). Iterator blocks mean the compiler has to create a nested class implementing IEnumerable and IEnumerator (well, more or less, if the iterator block is in the GetEnumerator method of an IEnumerable class, the nested class that gets created only needs to implement IEnumerator).
There is a sequence of steps to follow, first the compiler transforms all the Anonymous Methods, then the Iterator Blocks. When an Anonymous method contains an Iterator block we have a problem with that sequence...

You can check some code here

and to spice up this post, a Reflector screenshot showing some of the compiler generated code:

No comments:

Post a Comment