Anlässe, an denen die using-Anweisung und IDisposable niemals verwendet werden sollten

8

Ich habe über dieses Szenario gelesen, bei dem die Verwendung der C # using-Anweisung zu Problemen führen kann . Ausnahmen, die im Rahmen des using-Blocks ausgelöst werden, können verloren gehen, wenn die am Ende der using-Anweisung aufgerufene Dispose-Funktion ebenfalls eine Exception auslösen würde. Dies zeigt, dass in bestimmten Fällen bei der Entscheidung, ob die using-Anweisung hinzugefügt werden soll, besondere Vorsicht geboten ist.

Ich neige nur dazu, Anweisungen zu verwenden, wenn ich Streams und die von DbConnection abgeleiteten Klassen benutze. Wenn ich nicht verwaltete Ressourcen bereinigen möchte, würde ich normalerweise einen finally Block bevorzugen.

Dies ist eine weitere Verwendung der IDisposable-Schnittstelle, um einen Leistungszeitgeber zu erstellen, der den Zeitgeber stoppt und die Zeit in der Registrierung in der Dispose-Funktion protokolliert. Ссылка

Ist das die IDisposable-Schnittstelle? Ressourcen werden nicht aufgeräumt und keine weiteren Objekte entsorgt. Ich kann jedoch sehen, wie es den aufrufenden Code aufräumen könnte, indem man den Code, den es profiliert, in eine using-Anweisung einbindet.

Gibt es Zeiten, in denen die using-Anweisung und die IDisposable-Schnittstelle niemals verwendet werden sollten? Hat die Implementierung von IDisposable oder Wrapping-Code in einer using-Anweisung schon einmal Probleme für Sie verursacht?

Danke

    
fletcher 20.07.2010, 17:40
quelle

5 Antworten

5

Ich würde sagen, verwenden Sie immer using , es sei denn, die Dokumentation sagt Ihnen nicht (wie in Ihrem Beispiel).

Wenn eine Dispose -Methode Ausnahmen auslöst, wird der Punkt, an dem sie verwendet wird, vereitelt (Wortspiel beabsichtigt). Immer wenn ich es implementiere, versuche ich immer sicherzustellen, dass keine Ausnahmen daraus ausgelöst werden, unabhängig davon, in welchem ​​Zustand sich das Objekt befindet.

PS: Hier ist eine einfache Hilfsmethode, um das WCF-Verhalten zu kompensieren. Dadurch wird sichergestellt, dass Abort in jedem Ausführungspfad aufgerufen wird, außer wenn Close aufgerufen wird und dass Fehler bis zum Aufrufer fortgeschrieben werden.

%Vor%

Wenn Sie an einem anderen Ort im Framework andere Fälle von witzigem Verhalten finden, können Sie eine ähnliche Strategie für den Umgang mit ihnen entwickeln.

    
Christian Hayter 20.07.2010, 17:46
quelle
3

Die allgemeine Faustregel ist einfach: Wenn eine Klasse IDisposable implementiert, verwenden Sie using . Wenn Sie Fehler erkennen müssen, verwenden Sie try / catch / finally , um die Fehler abfangen zu können.

Ein paar Beobachtungen jedoch.

  1. Sie fragen, ob Situationen existieren, in denen IDisposable nicht verwendet werden sollte. Nun, in den meisten Situationen sollten Sie es nicht implementieren müssen. Verwenden Sie es, wenn Sie Ressourcen rechtzeitig freigeben möchten, anstatt zu warten, bis der Finalizer startet.

  2. Wenn IDisposable implementiert ist, sollte dies bedeuten, dass die entsprechende Dispose-Methode ihre eigenen Ressourcen löscht und alle referenzierten oder zugehörigen Objekte durchläuft und Dispose für sie aufruft. Es sollte auch kennzeichnen, ob Dispose bereits aufgerufen wurde, um zu verhindern, dass mehrere Bereinigungen oder referenzierte Objekte dasselbe tun, was zu einer Endlosschleife führt. All dies ist jedoch keine Garantie dafür, dass alle Verweise auf das aktuelle Objekt nicht mehr vorhanden sind. Dies bedeutet, dass es im Speicher verbleibt, bis alle Referenzen entfernt wurden und der Finalizer gestartet wird.

  3. Das Auslösen von Ausnahmen in Dispose ist verpönt und wenn es passiert, ist der Zustand möglicherweise nicht mehr gewährleistet. Eine unangenehme Situation, in der du dich befinden kannst. Du kannst es mit try / catch / finally beheben und im finally Block einen weiteren try / catch hinzufügen. Aber wie gesagt: Das wird schnell hässlich.

  4. Die Verwendung von using ist eine Sache, aber verwechseln Sie sie nicht mit try / finally . Beide sind gleich, aber die using-Anweisung macht das Leben einfacher, indem man Scoping und Null-Checks hinzufügt, was jedes Mal mühsam ist. Die using-Anweisung übersetzt dies (ab C # -Standard):

    %Vor%
  5. Es gibt Situationen, in denen das Umbrechen eines Objekts in einen using-Block nicht erforderlich ist. Diese Anlässe sind selten. Sie treten auf, wenn Sie feststellen, dass Sie von einer Schnittstelle erben, die von IDisposable erbt, nur für den Fall, dass ein Kind entsorgt werden müsste. Ein häufig verwendetes Beispiel ist IComponent, das mit jedem Control verwendet wird (Form, EditBox, UserControl, Sie nennen es). Und ich sehe selten, dass Leute all diese Steuerelemente in using-Anweisungen einschließen. Ein anderes berühmtes Beispiel ist IEnumerator<T> . Wenn man seine Nachkommen benutzt, sieht man selten die Verwendung von Blöcken.

Fazit

Verwenden Sie die using-Anweisung ubiquitär und achten Sie auf Alternativen oder lassen Sie sie außen vor. Stellen Sie sicher, dass Sie die Implikationen dessen kennen, wie Sie es (nicht) verwenden, und beachten Sie die Gleichheit von using und try / finally. Müssen Sie etwas fangen? Verwenden Sie try / catch / finally.

    
Abel 20.07.2010 18:11
quelle
2

Ich denke, das größere Problem ist das Auslösen von Ausnahmen in Dispose . RAII-Muster geben normalerweise explizit an, dass dies nicht gemacht werden sollte, da es Situationen wie diese erzeugen kann. Ich meine, was ist der Recovery-Pfad für etwas, das nicht richtig disponiert, außer einfach die Ausführung zu beenden?

Auch das scheint mit zwei try-catch-Anweisungen vermieden zu werden:

%Vor%

Das einzige Problem, das hier auftreten kann, ist, ob DisposeException gleich oder ein Supertyp von NonDisposeException ist und Sie versuchen, den% code_% catch erneut zu entfernen. In diesem Fall wird der NonDisposeException -Block sie abfangen. Sie benötigen also möglicherweise einen zusätzlichen booleschen Marker, um dies zu überprüfen.

    
jdmichal 20.07.2010 17:49
quelle
2

Der einzige Fall, den ich kenne, sind WCF-Clients. Das liegt an einem Designfehler in WCF - Dispose sollte niemals Ausnahmen auslösen. Sie haben das vermisst.

    
John Saunders 20.07.2010 18:24
quelle
1

Ein Beispiel ist die Eigenschaft IAsyncResult.AsyncWaitHandle . Der scharfsinnige Programmierer wird erkennen, dass WaitHandle classes IDisposable implementieren und natürlich versuchen, sie gierig zu beseitigen. Abgesehen davon, dass die meisten Implementierungen des APM in der BCL tatsächlich die Initialisierung von WaitHandle in der Eigenschaft verzögern. Offensichtlich hat der Programmierer mehr Arbeit geleistet als nötig.

Wo ist der Zusammenbruch? Nun, Microsoft hat die IAsyncResult Schnittstelle vermasselt. Hätten sie ihrem eigenen Rat gefolgt, hätte IAsyncResult von IDisposable abgeleitet, da die Implikation darin besteht, dass sie verfügbare Ressourcen enthält. Der scharfsinnige Programmierer würde dann einfach Dispose auf dem IAsyncResult aufrufen und ihn entscheiden lassen, wie seine Bestandteile am besten zu entsorgen sind.

Dies ist einer der klassischen Randfälle, bei denen die Entsorgung von IDisposable problematisch sein könnte. Jeffrey Richter verwendet dieses Beispiel tatsächlich, um zu argumentieren (fälschlicherweise meiner Meinung nach), dass der Aufruf von Dispose nicht obligatorisch ist. Sie können die Debatte hier lesen.

    
Brian Gideon 20.07.2010 17:59
quelle

Tags und Links