Im Grunde war es ein Versehen. In C # 1.0, foreach
nie genannt Dispose
1 . Mit C # 1.2 (eingeführt in VS2003 - es gibt keine 1.1, bizarrerweise) foreach
begann im finally
Block zu prüfen , ob der Iterator implementiert war oder nicht IDisposable
- sie mussten dies auf diese Weise tun, da eine nachträgliche IEnumerator
Erweiterung IDisposable
die Implementierung aller gebrochen hätte IEnumerator
. Wenn sie herausgefunden hätten, dass es nützlich ist foreach
, Iteratoren überhaupt zu entsorgen, hätte ich mich sicher IEnumerator
erweitert IDisposable
.
Als C # 2.0 und .NET 2.0 herauskamen, hatten sie jedoch eine neue Gelegenheit - neue Schnittstelle, neue Vererbung. Es ist viel sinnvoller, die Schnittstelle IDisposable
so zu erweitern , dass Sie keine Ausführungszeitprüfung im finally-Block benötigen, und jetzt weiß der Compiler, dass der Iterator, wenn er ein Iterator ist, IEnumerator<T>
einen bedingungslosen Aufruf an senden kann Dispose
.
BEARBEITEN: Es ist unglaublich nützlich Dispose
, am Ende der Iteration aufgerufen zu werden (wie auch immer es endet). Dies bedeutet, dass der Iterator an Ressourcen festhalten kann - was es ihm ermöglicht, beispielsweise eine Datei Zeile für Zeile zu lesen. Iteratorblöcke generieren Dispose
Implementierungen, die sicherstellen, dass alle finally
Blöcke, die für den "aktuellen Ausführungspunkt" des Iterators relevant sind, ausgeführt werden, wenn er entsorgt wird. Sie können also normalen Code in den Iterator schreiben und die Bereinigung sollte angemessen erfolgen.
1 Rückblickend auf die 1.0-Spezifikation wurde sie bereits spezifiziert. Ich konnte diese frühere Aussage, dass die 1.0-Implementierung nicht aufgerufen wurde, noch nicht überprüfen Dispose
.
IEnumerable.GetEnumerator
(nicht generischer) vorhanden istIDisposable
?IEnumerable
ist verpflichtet, sicherzustellen, dass alle von zurückgegebenen EinwegobjekteGetEnumerator
entsorgt werden. Code, der nicht als fehlerhaft angesehen werden sollte.Dispose
Logik vorhanden war,foreach
hätte sich gegenüber derVisualBasic.Collection
Klasse sehr schlecht verhalten (was in vielerlei Hinsicht ärgerlich und eigenartig ist, aber - anders als bei allen anderen Microsoft-Sammlungen dieser Zeit - das Entfernen von Elementen ermöglicht während der Aufzählung). DieCollection
Klasse vermeidet es, starke Verweise auf herausragende Enumeratoren zu speichern, und bereinigt sie, wenn sie durch Müll gesammelt werden. Wenn eine Sammlung jedoch zwischen den GC-Zyklen viele Male aufgelistet wird und diese Enumeratoren nicht bereinigt werden, wird sie sehr langsam.IEnumerable <T> erbt IDisposable nicht. IEnumerator <T> erbt jedoch IDisposable, während der nicht generische IEnumerator dies nicht tut. Selbst wenn Sie foreach für eine nicht generische IEnumerable verwenden (die IEnumerator zurückgibt), generiert der Compiler dennoch eine Prüfung auf IDisposable und ruft Dispose () auf, wenn der Enumerator die Schnittstelle implementiert.
Ich denke, der generische Enumerator <T> erbt von IDisposable, so dass keine Laufzeit-Typprüfung erforderlich ist. Er kann einfach Dispose () aufrufen, das eine bessere Leistung haben sollte, da es wahrscheinlich optimiert werden kann, wenn das Der Enumerator hat eine leere Dispose () -Methode.
quelle
IEnumerable
at-Kompilierung so kennt , dass er den Aufruf anDispose
optimieren kann, kann er auch eine Typprüfung optimieren.Ich weiß, dass dies eine alte Diskussion ist, aber ich habe erneut eine Bibliothek geschrieben, in der ich IEnumerable von T / IEnumerator von T verwendet habe, wobei Benutzer der Bibliothek benutzerdefinierte Iteratoren implementieren konnten, die nur IEnumerator von T implementieren sollten.
Ich fand es sehr seltsam, dass IEnumerator von T von IDisposable erben würde. Wir implementieren IDisposable, wenn wir unveränderte Ressourcen freigeben möchten, oder? Es wäre also nur für Enumeratoren relevant, die tatsächlich nicht verwaltete Ressourcen enthalten - wie z. B. einen E / A-Stream usw. Warum lassen Sie Benutzer nicht einfach sowohl IEnumerator of T als auch IDisposable auf ihrem Enumerator implementieren, wenn dies sinnvoll ist? In meinem Buch verstößt dies gegen das Prinzip der Einzelverantwortung - Warum Enumerator-Logik mischen und Objekte entsorgen?
quelle
GetEnumerator
ein Objekt zurückgegeben wird, das bereinigt werden muss (z. B. weil Datenzeilen aus einer Datei gelesen werden, die geschlossen werden muss), muss eine Entität, die weiß, wann der Enumerator nicht mehr benötigt wird, über eine Möglichkeit verfügen, diese Informationen an eine Entität zu übermitteln, die dies kann Führen Sie die Bereinigung durch.IDisposable
verhält sich in Bezug auf das Liskov-Substitutionsprinzip rückwärts, da eine Fabrik, die Dinge zurückgibt, die möglicherweise bereinigt werden müssen, nicht sicher durch eine Fabrik ersetzt werden kann, die verspricht, Dinge zurückzugeben, die dies nicht tun, aber die umgekehrte Substitution wäre sicher und sollte legitim sein.IDisposable
aufIEnumerator<T>
ein wenig verwirrend zu sein, ich fand es hilfreich zu überprüfen, wie dieEnumerator
vonList<T>
implementiertIDisposable
in der .NET - Quelle. Beachten Sie, wie dasEnumerator
struct
eineDispose
Methode hat, aber es ist nichts drin. Beachten Sie, dass diesesIDisposable
Verhalten in keiner Weise impliziert, dass "AList<Bitmap>
seinBitmap
s in entsorgen sollteforeach
! "Erbt IEnumerable` IDisposing? Laut .NET Reflektor oder MSDN . Sind Sie sicher, dass Sie es nicht mit IEnumerator verwechseln ? Das verwendet IDisposing, weil es nur zum Auflisten einer Sammlung dient und nicht für die Langlebigkeit gedacht ist.
quelle
Es ist ein bisschen schwierig, diesbezüglich endgültig zu sein, es sei denn, Sie erhalten eine Antwort von AndersH selbst oder von jemandem, der ihm nahe steht.
Ich vermute jedoch, dass es sich um das Schlüsselwort "ield "handelt, das gleichzeitig in C # eingeführt wurde. Wenn Sie sich den vom Compiler generierten Code ansehen, wenn "Yield Return x" verwendet wird, sehen Sie die Methode in einer Hilfsklasse, die IEnumerator implementiert. Wenn IEnumerator von IDisposable abstammt, wird sichergestellt, dass er nach Abschluss der Aufzählung bereinigt werden kann.
quelle
VisualBasic.Collection
Klasse wird eine schlechte Leistung erbringen (mehrere Größenordnungen langsamer als normal), wenn viele Enumeratoren erstellt und aufgegeben werden, ohne dass sie entsorgt werden. Die Notwendigkeit, sichDispose
mit zu verbinden,IEnumerable
war somit offensichtlich, bevor die Veröffentlichung von.net 1.0, but probably late enough that changing
IEnumerable` zum ErbenIDisposable
den Veröffentlichungsplan beeinflusst hätte.IIRC Die ganze Sache mit
IEnumerable<T>
undIEnumerable
ist das Ergebnis vonIEnumerable
.Nets Vorlagenmaterial. Ich vermute, dass Ihre Frage genauso ist.quelle