Wird Dispose immer noch aufgerufen, wenn eine Ausnahme in einer using-Anweisung ausgelöst wird?

101

Wird im folgenden Beispiel die Verbindung geschlossen und getrennt, wenn eine Ausnahme ausgelöst wird, wenn sie sich in einer usingAnweisung befindet?

using (var conn = new SqlConnection("..."))
{
    conn.Open();
    // stuff happens here and exception is thrown...
}

Ich weiß, dass dieser Code unten sicherstellt, dass dies der Fall ist, aber ich bin gespannt, wie die Verwendung der Anweisung dies bewirkt.

var conn;
try
{
    conn = new SqlConnection("...");
    conn.Open();
    // stuff happens here and exception is thrown...
}
// catch it or let it bubble up
finally
{
    conn.Dispose();
}

Verbunden:

Wie kann sichergestellt werden, dass eine SQL-Verbindung geschlossen wird, wenn eine Ausnahme ausgelöst wird?

Brian Kim
quelle

Antworten:

110

Ja, usingverpackt Ihren Code in einen try / finally-Block, in dem der finallyTeil aufgerufen wird, Dispose()falls vorhanden. Es wird jedoch nicht Close()direkt aufgerufen, da nur die IDisposableimplementierte Schnittstelle und damit die Dispose()Methode überprüft wird.

Siehe auch:

Jeff Yates
quelle
5
Nur um auf die Verbindungsklassen hinzuweisen, wenn Sie über sie reflektieren, sehen Sie, dass Dispose () tatsächlich intern Close () aufruft. Wenn es in einem Zustand ist, kann es.
Chris Marisic
2
Sie haben Recht, das tut es. Ich habe es jedoch absichtlich nicht erwähnt, da ich niemanden irreführen wollte, um zu glauben, dass dies etwas mit IDisposable oder dem damit verbundenen Muster zu tun hat. Die Tatsache, dass diese bestimmte Implementierung Close () aufruft, ist ein Detail der Implementierung, nicht des Musters.
Jeff Yates
3
Die Verwendung der Dokumentation zu MSDN bestätigt auch diese Antwort: Die using-Anweisung stellt sicher, dass Dispose aufgerufen wird, auch wenn beim Aufrufen von Methoden für das Objekt eine Ausnahme auftritt. Sie können das gleiche Ergebnis erzielen, indem Sie das Objekt in einen try-Block einfügen und dann Dispose in einem finally-Block aufrufen. Auf diese Weise wird die using-Anweisung vom Compiler übersetzt.
Breitband
20

So decodiert der Reflektor die von Ihrem Code erzeugte IL:

private static void Main (string [] args)
{
    SqlConnection conn = new SqlConnection ("...");
    Versuchen
    {
        conn.Open ();
        Sachen machen();
    }}
    endlich
    {
        if (conn! = null)
        {
            conn.Dispose ();
        }}
    }}
}}

Die Antwort lautet also: Ja, die Verbindung wird geschlossen, wenn

Sachen machen()
löst eine Ausnahme aus.

Florin Sabau
quelle
Hinzufügen, wenn conn.Open () eine Ausnahme auslöst. : D
Jeff Yates
Ja sicher. Wenn irgendetwas im Block NACH der using-Klausel eine Ausnahme auslöst, wird die Verbindung geschlossen. Die einzige Möglichkeit, den finally-Block nicht auszuführen, besteht darin, dass "new SqlConnection (...)" ausgelöst wird. In diesem Fall hätten Sie jedoch keine gültige offene Verbindung zum Schließen. Also ist es gut.
Florin Sabau
-1

Dispose () wird in diesem Code nicht aufgerufen.

class Program {
    static void Main(string[] args) {
        using (SomeClass sc = new SomeClass())
        {
            string str = sc.DoSomething();
            sc.BlowUp();
        }
    }
}

public class SomeClass : IDisposable {
    private System.IO.StreamWriter wtr = null;

    public SomeClass() {
        string path = System.IO.Path.GetTempFileName();
        this.wtr = new System.IO.StreamWriter(path);
        this.wtr.WriteLine("SomeClass()");
    }

    public void BlowUp() {
        this.wtr.WriteLine("BlowUp()");
        throw new Exception("An exception was thrown.");
    }

    public string DoSomething() {
        this.wtr.WriteLine("DoSomething()");
        return "Did something.";
    }

    public void Dispose() {
        this.wtr.WriteLine("Dispose()");
        this.wtr.Dispose();
    }
}
Tschad
quelle
Beantwortet dies die OP-Frage?
Joey Phillips
Ja. Die Antwort ist nein. Dispose () wird im angehängten Code nicht aufgerufen. Außerdem wird die ausgelöste Ausnahme nicht behandelt und das Programm explodiert.
Tschad
Sie müssen sich die falsche Datei ansehen. "Dispose ()" wird in Ihre temporäre Datei geschrieben. Niemand behauptet, dass ein using-Block eine Ausnahme behandelt. Versuchen Sie dies ohne Debugger auszuführen.
LarsTech
Ich habe genau den gleichen Code ausgeführt und er ruft Dispose () auf. Sind Sie sicher, dass Ihre Antwort richtig ist?
Dnomyar96