80

It's recently been pointed out to me that various Linq extension methods (such as Where, Select, etc) return an IEnumerable<T> that also happens to be IDisposable. The following evaluates to True

new int[2] {0,1}.Select(x => x*2) is IDisposable

Do I need to dispose of the results of a Where expression?

Whenever I call a method returning IEnumerable<T>, am I (potentially) accepting responsibility for calling dispose when I've finished with it?

Dave New
  • 38,496
  • 59
  • 215
  • 394
Rob
  • 4,327
  • 6
  • 29
  • 55

1 Answers1

94

No, you don't need to worry about this.

The fact that they return an IDisposable implementation is an implementation detail - it's because iterator blocks in the Microsoft implementation of the C# compiler happen to create a single type which implements both IEnumerable<T> and IEnumerator<T>. The latter extends IDisposable, which is why you're seeing it.

Sample code to demonstrate this:

using System;
using System.Collections.Generic;

public class Test 
{
    static void Main() 
    {
        IEnumerable<int> foo = Foo();
        Console.WriteLine(foo is IDisposable); // Prints True
    }

    static IEnumerable<int> Foo()
    {
        yield break;
    }
}

Note that you do need to take note of the fact that IEnumerator<T> implements IDisposable. So any time you iterate explicitly, you should dispose of it properly. For example, if you want to iterate over something and be sure that you'll always have a value, you might use something like:

using (var enumerator = enumerable.GetEnumerator())
{
    if (!enumerator.MoveNext())
    {
        throw // some kind of exception;
    }
    var value = enumerator.Current;
    while (enumerator.MoveNext())
    {
        // Do something with value and enumerator.Current
    }
}

(A foreach loop will do this automatically, of course.)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 2
    So provided I don't use the enumerator explicitly, I never need to dispose? – Rob Nov 19 '12 at 18:18
  • 2
    It's interesting to note that calling `Dispose` on an interator which has had `GetEnumerator()` called at least once will invalidate the enumerator returned by the first call, but not any enumerators returned by subsequent calls; calling `Dispose` before the first `GetEnumerator()` call has no effect. – supercat Nov 19 '12 at 22:32
  • 1
    @JonSkeet Do you happen to have any insight into the odd behavior described by `supercat`? – ean5533 Nov 24 '12 at 04:22
  • 3
    @ean5533: The first call to `GetEnumerator()` returns a reference to the same reference; thereafter, it will create separate copies. The idea is to only create a single object in the common case of iterating exactly once. – Jon Skeet Nov 24 '12 at 07:50
  • 1
    Instead of checking true on Enumerator.MoveNext(), use Enumerator.FirstOrDefault() != null. This encapsulates disposing if required to the framework. Which is safe when working with multithreading aware collections. – TamusJRoyce Mar 10 '15 at 16:31
  • 1
    @TamusJRoyce: Which Enumerator class is that? And what if the sequence is of a non-nullable value type? Or if it is a sequence with a first value of null? – Jon Skeet Mar 10 '15 at 16:35
  • 1
    If I use the nongereric version do I need to dispose? @JonSkeet – regisbsb Sep 16 '15 at 12:51
  • 1
    @regisbsb: The nongeneric version of what? `IEnumerable`? You *should*, IMO - `foreach` will do that automatically for you, for example. – Jon Skeet Sep 16 '15 at 13:12
  • `IEnumerator`. So I need to check if the result of `GetEnumerator()` from `IEnumerable` is `IDisposable` then if it is `Dispose` it? – regisbsb Sep 16 '15 at 13:41
  • @regisbsb: I would view that as a best practice, yes. – Jon Skeet Sep 16 '15 at 13:51