Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When using "yield" why does compiler-generated type implement both IEnumerable and IEnumerator

We are trying to use an IEnumerable as a factory that generates different objects each time we iterate over it. Those should be GC'ed as soon as possible. Note however that we keep a reference to the enumerator so we can call it again. So our program basically looks like this:

public class YieldSpec
{
    public static IEnumerable<string> Strings()
    {
        yield return "AAA";
        yield return "BBB";
        yield return "CCC";
    } 
    public void YieldShouldAllowGC()
    {
        var e = Strings();
        foreach (var a in e)
        {
            Console.WriteLine(a);
        }

    }
}

Looking at that code in the debugger:

enter image description here

You can see that when the breakpoint is hit the IEnumerable has a reference to "CCC".

This shouldn't really happen. IEnumerable should only generate an IEnumerator when GetEnumerator is called. Is this expected behavior that IEnumerable can contain state?

like image 817
Johannes Egger Avatar asked Oct 05 '15 12:10

Johannes Egger


1 Answers

As an implementation detail for performance reasons, yes, the state machine implements both IEnumerable and IEnumerator. That said, it is smart enough to do this correctly. It is only the very first time that the IEnumerable is asked for an IEnumerator that it returns itself. Any future calls to GetEnumerator result in a new instance of the object being created, so that the separate iterator states can be maintained. This is done because while it is important to be able to have an IEnumerable create multiple IEnumerators, the vast majority of actual situations involve exactly one being created, so that's the situation that was optimized for.

like image 177
Servy Avatar answered Oct 14 '22 06:10

Servy