Muss ich in Betracht ziehen, alle von mir verwendeten IEnumerable <T> zu entsorgen?

70

Es ist mir kürzlich, dass Methoden verschiedene Linq Erweiterung hingewiesen (wie Where, Selectusw.) zurückgeben ein , IEnumerable<T>das auch passiert zu sein IDisposable. Das Folgende wird ausgewertetTrue

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

Muss ich über die Ergebnisse eines WhereAusdrucks verfügen ?

Wenn ich eine zurückkehrende Methode aufrufe IEnumerable<T>, übernehme ich (möglicherweise) die Verantwortung für den Aufruf von dispose, wenn ich damit fertig bin?

rauben
quelle

Antworten:

85

Nein, darüber brauchen Sie sich keine Sorgen zu machen.

Die Tatsache, dass sie eine IDisposableImplementierung zurückgeben, ist ein Implementierungsdetail. Dies liegt daran, dass Iteratorblöcke in der Microsoft-Implementierung des C # -Compilers zufällig einen einzelnen Typ erstellen, der sowohl IEnumerable<T>als auch implementiert IEnumerator<T>. Letzteres erstreckt sich IDisposable, weshalb Sie es sehen.

Beispielcode, um dies zu demonstrieren:

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;
    }
}

Beachten Sie, dass Sie keine Kenntnis von der Tatsache , dass ergreifen müssen IEnumerator<T>implementiert IDisposable. Jedes Mal, wenn Sie explizit iterieren, sollten Sie es ordnungsgemäß entsorgen. Wenn Sie beispielsweise über etwas iterieren und sicherstellen möchten, dass Sie immer einen Wert haben, können Sie Folgendes verwenden:

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
    }
}

(Eine foreachSchleife erledigt dies natürlich automatisch.)

Jon Skeet
quelle
2
Vorausgesetzt, ich verwende den Enumerator nicht explizit, muss ich ihn nie entsorgen?
Rob
1
Es ist interessant festzustellen, dass das Aufrufen Disposeeines Interators, der GetEnumerator()mindestens einmal aufgerufen hat , den vom ersten Aufruf zurückgegebenen Enumerator ungültig macht, jedoch keine von nachfolgenden Aufrufen zurückgegebenen Enumeratoren. Ein Anruf Disposevor dem ersten GetEnumerator()Anruf hat keine Auswirkung.
Supercat
@ JonSkeet Haben Sie zufällig einen Einblick in das seltsame Verhalten, das von beschrieben wird supercat?
ean5533
2
@ ean5533: Der erste Aufruf, GetEnumerator()der eine Referenz auf dieselbe Referenz zurückgibt; Danach werden separate Kopien erstellt. Die Idee ist, nur ein einziges Objekt zu erstellen, wenn häufig nur einmal iteriert wird.
Jon Skeet
Verwenden Sie Enumerator.FirstOrDefault ()! = Null, anstatt true in Enumerator.MoveNext () zu überprüfen. Dies kapselt die Entsorgung, falls erforderlich, für das Framework. Dies ist sicher, wenn Sie mit Multithreading-fähigen Sammlungen arbeiten.
TamusJRoyce