[ACCEPTED]-In C#, why can't an anonymous method contain a yield statement?-yield-return

Accepted answer
Score: 117

Eric Lippert recently wrote a series of 55 blog posts about why yield is not allowed 54 in some cases.

EDIT2:

  • Part 7 (this one was posted later and specifically addresses this question)

You will probably find 53 the answer there...


EDIT1: this is explained 52 in the comments of Part 5, in Eric's answer 51 to Abhijeet Patel's comment:

Q :

Eric,

Can 50 you also provide some insight into why "yields" are 49 not allowed inside an anonymous method or 48 lambda expression

A :

Good question. I would 47 love to have anonymous iterator blocks. It 46 would be totally awesome to be able to build yourself 45 a little sequence generator in-place that 44 closed over local variables. The reason 43 why not is straightforward: the benefits 42 don't outweigh the costs. The awesomeness 41 of making sequence generators in-place is actually 40 pretty small in the grand scheme of things 39 and nominal methods do the job well enough 38 in most scenarios. So the benefits are not that 37 compelling.

The costs are large. Iterator rewriting 36 is the most complicated transformation in 35 the compiler, and anonymous method rewriting 34 is the second most complicated. Anonymous methods 33 can be inside other anonymous methods, and 32 anonymous methods can be inside iterator 31 blocks. Therefore, what we do is first we 30 rewrite all anonymous methods so that they 29 become methods of a closure class. This 28 is the second-last thing the compiler does 27 before emitting IL for a method. Once that 26 step is done, the iterator rewriter can 25 assume that there are no anonymous methods 24 in the iterator block; they've all be rewritten already. Therefore 23 the iterator rewriter can just concentrate 22 on rewriting the iterator, without worrying 21 that there might be an unrealized anonymous 20 method in there.

Also, iterator blocks never 19 "nest", unlike anonymous methods. The 18 iterator rewriter can assume that all iterator blocks 17 are "top level".

If anonymous methods 16 are allowed to contain iterator blocks, then 15 both those assumptions go out the window. You 14 can have an iterator block that contains 13 an anonymous method that contains an anonymous 12 method that contains an iterator block that contains 11 an anonymous method, and... yuck. Now we 10 have to write a rewriting pass that can 9 handle nested iterator blocks and nested 8 anonymous methods at the same time, merging 7 our two most complicated algorithms into 6 one far more complicated algorithm. It would be 5 really hard to design, implement, and test. We 4 are smart enough to do so, I'm sure. We've 3 got a smart team here. But we don't want 2 to take on that large burden for a "nice 1 to have but not necessary" feature. -- Eric

Score: 22

Eric Lippert has written an excellent series 24 of articles on the limitations (and design 23 decisions influencing those choices) on 22 iterator blocks

In particular iterator blocks are implemented 21 by some sophisticated compiler code transformations. These 20 transformations would impact with the transformations 19 which happen inside anonymous functions 18 or lambdas such that in certain circumstances 17 they would both try to 'convert' the code 16 into some other construct which was incompatible 15 with the other.

As a result they are forbidden 14 from interaction.

How iterator blocks work 13 under the hood is dealt with well here.

As a 12 simple example of an incompatibility:

public IList<T> GreaterThan<T>(T t)
{
    IList<T> list = GetList<T>();
    var items = () => {
        foreach (var item in list)
            if (fun.Invoke(item))
                yield return item; // This is not allowed by C#
    }

    return items.ToList();
}

The 11 compiler is simultaneously wanting to convert 10 this to something like:

// inner class
private class Magic
{
    private T t;
    private IList<T> list;
    private Magic(List<T> list, T t) { this.list = list; this.t = t;}

    public IEnumerable<T> DoIt()
    {
        var items = () => {
            foreach (var item in list)
                if (fun.Invoke(item))
                    yield return item;
        }
    }
}

public IList<T> GreaterThan<T>(T t)
{
    var magic = new Magic(GetList<T>(), t)
    var items = magic.DoIt();
    return items.ToList();
}

and at the same time 9 the iterator aspect is trying to do it's 8 work to make a little state machine. Certain 7 simple examples might work with a fair amount 6 of sanity checking (first dealing with the 5 (possibly arbitrarily) nested closures) then 4 seeing if the very bottom level resulting 3 classes could be transformed into iterator 2 state machines.

However this would be

  1. Quite a lot of work.
  2. Couldn't possibly work in all cases without at the very least the iterator block aspect being able to prevent the closure aspect from applying certain transformations for efficiency (like promoting local variables to instance variables rather than a fully fledged closure class).
    • If there was even a slight chance of overlap where it was impossible or sufficiently hard to not be implemented then the number of support issues resulting would likely be high since the subtle breaking change would be lost on many users.
  3. It can be very easily worked around.

In 1 your example like so:

public IList<T> Find<T>(Expression<Func<T, bool>> expression) 
    where T : class, new()
{
    return FindInner(expression).ToList();
}

private IEnumerable<T> FindInner<T>(Expression<Func<T, bool>> expression) 
    where T : class, new()
{
    IList<T> list = GetList<T>();
    var fun = expression.Compile();
    foreach (var item in list)
        if (fun.Invoke(item))
            yield return item;
}
Score: 5

Unfortunately I don't know why they didn't 20 allow this, since of course it's entirely 19 possible to do envision how this would work.

However, anonymous 18 methods are already a piece of "compiler 17 magic" in the sense that the method 16 will be extracted either to a method in 15 the existing class, or even to a whole new 14 class, depending on whether it deals with 13 local variables or not.

Additionally, iterator 12 methods using yield is also implemented using 11 compiler magic.

My guess is that one of these 10 two makes the code un-identifiable to the 9 other piece of magic, and that it was decided 8 to not spend time on making this work for 7 the current versions of the C# compiler. Of 6 course, it might not be a concious choice 5 at all, and that it just doesn't work because 4 nobody thought to implement it.

For a 100% accurate 3 question I would suggest you use the Microsoft Connect site 2 and report a question, I'm sure you'll get 1 something usable in return.

Score: 1

I would do this:

IList<T> list = GetList<T>();
var fun = expression.Compile();

return list.Where(item => fun.Invoke(item)).ToList();

Of course you need the System.Core.dll 2 referenced from .NET 3.5 for the Linq method. And 1 include:

using System.Linq;

Cheers,

Sly

Score: 0

Maybe its just a syntax limitation. In Visual 18 Basic .NET, which is very similar to C#, it 17 is perfectly possible while awkward to write

Sub Main()
    Console.Write("x: ")
    Dim x = CInt(Console.ReadLine())
    For Each elem In Iterator Function()
                         Dim i = x
                         Do
                             Yield i
                             i += 1
                             x -= 1
                         Loop Until i = x + 20
                     End Function()
        Console.WriteLine($"{elem} to {x}")
    Next
    Console.ReadKey()
End Sub

Also 16 note the parentheses ' here; the lambda function 15 Iterator Function...End Function returns an IEnumerable(Of Integer) but is not such an object itself. It 14 must be called to get that object.

The converted 13 code by [1] raises errors in C# 7.3 (CS0149):

static void Main()
{
    Console.Write("x: ");
    var x = System.Convert.ToInt32(Console.ReadLine());
    // ERROR: CS0149 - Method name expected 
    foreach (var elem in () =>
    {
        var i = x;
        do
        {
            yield return i;
            i += 1;
            x -= 1;
        }
        while (!i == x + 20);
    }())
        Console.WriteLine($"{elem} to {x}");
    Console.ReadKey();
}

I 12 strongly disagree to the reason given in 11 the other answers that it's difficult for 10 the compiler to handle. The Iterator Function() you see in 9 the VB.NET example is specifically created 8 for lambda iterators.

In VB, there is the 7 Iterator keyword; it has no C# counterpart. IMHO, there 6 is no real reason this is not a feature 5 of C#.

So if you really, really want anonymous 4 iterator functions, currently use Visual 3 Basic or (I haven't checked it) F#, as stated 2 in a comment of Part #7 in @Thomas Levesque's answer 1 (do Ctrl+F for F#).

More Related questions