Was passiert, wenn ich vor dem Ende der Verwendung der Anweisung zurückkehre? Wird die Entsorgung aufgerufen?


115

Ich habe den folgenden Code

using(MemoryStream ms = new MemoryStream())
{
     //code
     return 0;
}

Die dispose()Methode wird am Ende von usingAnweisungsklammern aufgerufen, }oder? Wird das Objekt ordnungsgemäß entsorgt, da ich returnvor dem Ende der usingAnweisung bin MemoryStream? was geschieht hier?


4
@ JonH: Finden Sie das genaue Duplikat und stimmen Sie dann ab, um es in diesem Fall zu schließen.
Noldorin

@Noldorin: Ich ging auf diesem für eine Betrogene suchen, weil ich es dachte , muss vor gefragt worden, aber ich war nicht in der Lage , einen zu finden. Ich denke, es gibt immer noch einfache Fragen. :)
Randolpho

@JonH und @Noldorin - Duplikate wären bei der Erstellung der Frage präsentiert worden. Sie suchen nach "ähnlichen Fragen", eine Funktion, die anscheinend nicht ausreichend genutzt wird.
Adam Houldsworth

@Adam: probier es selbst aus. Kopieren Sie den Titel, fügen Sie ihn ein und sehen Sie, welche Duplikate vom System angezeigt werden. Ich gebe Ihnen einen Hinweis: Die Antwort ist keine. Das Gleiche gilt, wenn Sie über die Suche von Google oder SO suchen. Es scheint, dass diese Frage noch nicht gestellt wurde.
Randolpho

Aaap ... ich nehme das zurück. Nach einer sehr engagierten Suche habe ich gerade ein Duplikat gefunden: stackoverflow.com/questions/2641692/… Nun wird die Frage ganz anders gestellt, aber die letzte Frage ist ziemlich gleich. Ich nehme an, wir können das doch als Betrug betrachten.
Randolpho

Antworten:


167

Ja, Disposewird angerufen. Es wird aufgerufen, sobald die Ausführung den Bereich des usingBlocks verlässt , unabhängig davon, welche Mittel zum Verlassen des Blocks erforderlich waren, sei es das Ende der Ausführung des Blocks, eine returnAnweisung oder eine Ausnahme.

Wie @Noldorin richtig hervorhebt, wird die Verwendung eines usingBlocks im Code in try/ kompiliert finally, Disposewobei im finallyBlock aufgerufen wird . Zum Beispiel der folgende Code:

using(MemoryStream ms = new MemoryStream())
{
     //code
     return 0;
}

effektiv wird:

MemoryStream ms = new MemoryStream();
try
{
    // code
    return 0;
}
finally
{
    ms.Dispose();
}

Da finallydie Ausführung garantiert ist, nachdem der tryBlock die Ausführung beendet hat, wird unabhängig von seinem Ausführungspfad Disposegarantiert, dass er aufgerufen wird, egal was passiert.

Weitere Informationen finden Sie in diesem MSDN-Artikel .

Nachtrag:
Nur eine kleine Einschränkung: Da Disposeder Aufruf garantiert ist, ist es fast immer eine gute Idee, sicherzustellen, dass Disposebei der Implementierung niemals eine Ausnahme ausgelöst wird IDisposable. Leider gibt es einige Klassen in der Kernbibliothek, die unter bestimmten Umständen auslösen, wenn sie Disposeaufgerufen werden - ich sehe Sie an, WCF-Dienstreferenz / Client-Proxy! - und wenn dies passiert, kann es sehr schwierig sein, die ursprüngliche Ausnahme aufzuspüren, wenn Disposesie während eines Abwickelns des Ausnahmestapels aufgerufen wurde, da die ursprüngliche Ausnahme zugunsten der neuen Ausnahme verschluckt wird, die durch den DisposeAufruf generiert wird . Es kann unglaublich frustrierend sein. Oder ist das frustrierend verrückt? Einer der Beiden. Vielleicht beide.


4
Ich denke, Sie werden feststellen, dass es effektiv zu einem Try-finally-Block mit einem Aufruf von Disposein finally kompiliert wurde , sodass die Implementierung von effektiv funktioniert finally, wie Sie beschreiben.
Noldorin

@Noldorin: genau. Obwohl ich denke, dass ich darüber explizit sein könnte. Bearbeiten Sie in Kürze ....
Randolpho

1
Beachten Sie auch, dass unter bestimmten Umständen die Ausführung des finally-Blocks nicht garantiert werden kann, z. B. die Verwendung von Environment.FailFast und das Auftreten einer StackOverFlowException.
Christopher McAtackney

@ C.McAtackney: auch ein guter Punkt. Auch IIRC, OutOfMemoryException; Grundsätzlich wird Dispose nicht aufgerufen, wenn Sie die Ausnahme nicht abfangen können, weil es sich um einen kritischen Ausführungsfehler handelt. In einem solchen Fall stürzt das Programm natürlich zusammen mit dem ihm zugewiesenen Speicher ab. In 99,9% der Fälle ist dies kein Problem, es sei denn, Sie machen wackelige Dinge wie das Schreiben in eine Datei in Ihrer Entsorgungsmethode . Abgesehen von dem katastrophalen Programmabsturz.
Randolpho

Sie sollten die Anweisung 'using ()' niemals mit WCF verwenden. Weitere Informationen finden Sie in diesem Artikel . Hier ist ein Ausschnitt, den ich für WCF-Proxys verwende: 'WCFProxy variableName = null; try {variableName = new WCFProxy (); // TODO-Code hier variableName.Proxy.Close (); variableName.Dispose (); } catch (Ausnahme) {if (variableName! = null && variableName.Proxy! = null) {variableName.Proxy.Abort (); } werfen; } '
Dave Black

18

usingAnweisungen verhalten sich genau wie try ... finallyBlöcke und werden daher immer auf Code-Exit-Pfaden ausgeführt. Ich glaube jedoch, dass sie den sehr wenigen und seltenen Situationen unterliegen, in denen finallyBlöcke nicht aufgerufen werden. Ein Beispiel, an das ich mich erinnern kann, ist, wenn der Vordergrund-Thread beendet wird, während Hintergrund-Threads aktiv sind: Alle Threads außer dem GC werden angehalten, was bedeutet finally, dass keine Blöcke ausgeführt werden.

Offensichtliche Bearbeitung: Abgesehen von der Logik, mit der sie mit IDisposable-Objekten umgehen können, verhalten sie sich gleich, d'oh.

Bonusinhalt: Sie können gestapelt werden (wobei sich die Typen unterscheiden):

using (SqlConnection conn = new SqlConnection("string"))
using (SqlCommand comm = new SqlCommand("", conn))
{

}

Und auch durch Kommas getrennt (wo die Typen gleich sind):

using (SqlCommand comm = new SqlCommand("", conn), 
       SqlCommand comm2 = new SqlCommand("", conn))
{

}

4

Ihr MemoryStream-Objekt wird ordnungsgemäß entsorgt, Sie müssen sich darüber keine Sorgen machen.



0

Sehen Sie sich Ihren Code im Reflektor an, nachdem Sie ihn kompiliert haben. Sie werden feststellen, dass der Compiler den Code umgestaltet, um sicherzustellen, dass dispose im Stream aufgerufen wird.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.