Czy Dispose nadal jest wywoływana, gdy wyjątek jest zgłaszany w instrukcji using?


103

Czy w poniższym przykładzie połączenie zostanie zamknięte i usunięte, gdy zostanie zgłoszony wyjątek, jeśli znajduje się w usinginstrukcji?

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

Wiem, że poniższy kod sprawi, że tak się stanie, ale jestem ciekaw, jak to robi za pomocą instrukcji.

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

Związane z:

Jaki jest właściwy sposób zapewnienia zamknięcia połączenia SQL w przypadku zgłoszenia wyjątku?

Odpowiedzi:


112

5
Aby wskazać na klasy połączeń, jeśli odbijasz się nad nimi, zobaczysz, że Dispose () rzeczywiście wewnętrznie wywołuje Close (). Jeśli jest w stanie, to może.
Chris Marisic

2
Masz rację, tak. Jednak celowo o tym nie wspomniałem, ponieważ nie chciałem nikogo wprowadzać w błąd, aby sądził, że ma to coś wspólnego z IDisposable lub powiązanym wzorcem. Fakt, że ta konkretna implementacja wywołuje Close (), jest szczegółem implementacji, a nie wzorcem.
Jeff Yates,

3
MSDN przy użyciu dokumentacji również potwierdza tę odpowiedź: Instrukcja using zapewnia wywołanie metody Dispose, nawet jeśli wystąpi wyjątek podczas wywoływania metod na obiekcie. Możesz osiągnąć ten sam wynik, umieszczając obiekt w bloku try, a następnie wywołując metodę Dispose w bloku final; w rzeczywistości jest to sposób tłumaczenia instrukcji using przez kompilator.
szerokopasmowy

20

Oto jak reflektor dekoduje IL wygenerowany przez twój kod:

private static void Main (string [] args)
{
    SqlConnection conn = new SqlConnection („...”);
    próbować
    {
        conn.Open ();
        Robić coś();
    }
    Wreszcie
    {
        if (conn! = null)
        {
            conn.Dispose ();
        }
    }
}

Więc odpowiedź brzmi tak, to zamknie połączenie, jeśli

Robić coś()
zgłasza wyjątek.


Dodaj, jeśli conn.Open () zgłasza wyjątek. : D
Jeff Yates,

Jasne. Jeśli cokolwiek jest w bloku, PO klauzuli using zgłosi wyjątek, połączenie zostanie zamknięte. Jedynym sposobem, w jaki ostatni blok nie zostanie wykonany, jest zgłoszenie "new SqlConnection (...)", ale w takim przypadku nie miałbyś faktycznie ważnego otwartego połączenia do zamknięcia. Więc jest dobrze.
Florin Sabau,

-1

Dispose () nie jest wywoływana w tym kodzie.

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

Czy to odpowiada na pytanie OP?
Joey Phillips

Tak. Odpowiedź brzmi nie. Dispose () nie jest wywoływana w załączonym kodzie. Co więcej, zgłoszony wyjątek nie jest obsługiwany i program wybucha.
Czad

Musisz patrzeć na niewłaściwy plik. „Dispose ()” zostanie zapisane w pliku tymczasowym. Nikt nie twierdzi, że blok using będzie obsługiwał wyjątek. Spróbuj uruchomić to bez debugera.
LarsTech

Uruchomiłem dokładnie ten sam kod i wywołuje Dispose (). Czy na pewno Twoja odpowiedź jest prawidłowa?
Dnomyar96
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.