Question

Just wondering why Enumerable.Range implements IDisposable.

I understand why IEnumerator<T> does, but IEnumerable<T> doesn't require it.


(I discovered this while playing with my .Memoise() implementation, which has statement like

if (enumerable is IDisposable)
    ((IDisposable)enumerable).Dispose();

in its "source finished" method that I had placed a breakpoint on out of curiousity, and was triggered by a test.)

Was it helpful?

Solution

Enumerable.Range uses yield return in its method body. The yield return statement produces an anonymous type that implements IDisposable, under the magic of the compiler, like this:

static IEnumerable<int> GetNumbers()
{
    for (int i = 1; i < 10; i += 2)
    {
        yield return i;
    }
}

After being compiled, there is an anonymous nested class like this:

[CompilerGenerated]
private sealed class <GetNumbers>d__0 
   : IEnumerable<int>, IEnumerable, IEnumerator<int>, IEnumerator, IDisposable
{
    //the implementation
    //note the interface is implemented explicitly
    void IDisposable.Dispose() { }
}

so the result is a IDisposable. In this example, the Dispose method leaves empty. I think the reason is that there is nothing need to be disposed. If you yield return a type that contains unmanaged resources, you may get a different compiling result. (NOT SURE about it)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top