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?
IEnumerable. IEnumerable<T> contains a single method that you must implement when implementing this interface; GetEnumerator, which returns an IEnumerator<T> object. The returned IEnumerator<T> provides the ability to iterate through the collection by exposing a Current property.
The dispose pattern is used for objects that implement the IDisposable interface, and is common when interacting with file and pipe handles, registry handles, wait handles, or pointers to blocks of unmanaged memory. This is because the garbage collector is unable to reclaim unmanaged objects.
IEnumerable is an interface defining a single method GetEnumerator() that returns an IEnumerator interface. It is the base interface for all non-generic collections that can be enumerated. This works for read-only access to a collection that implements that IEnumerable can be used with a foreach statement.
You can create a class that implements the IEnumerable<T> interface to expose source data as enumerable data. Your class that implements the IEnumerable(T) interface will require another class that implements the IEnumerator<T> interface to iterate through the source data.
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.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With