Dispose ainda é chamado quando uma exceção é lançada dentro de uma instrução using?

103

No exemplo abaixo, a conexão será fechada e descartada quando uma exceção for lançada se estiver dentro de uma usinginstrução?

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

Eu sei que o código abaixo irá garantir que sim, mas estou curioso para saber como o uso de instrução faz isso.

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

Relacionado:

Qual é a maneira correta de garantir que uma conexão SQL seja fechada quando uma exceção é lançada?

Brian Kim
fonte

Respostas:

112

Sim, usingenvolve seu código em um bloco try / finally onde a finallyparte chamará, Dispose()se existir. No entanto, ele não chamará Close()diretamente, pois apenas verifica a IDisposableinterface que está sendo implementada e, portanto, o Dispose()método.

Veja também:

Jeff Yates
fonte
5
Apenas para apontar nas classes de conexão, se você refletir sobre elas, verá que Dispose () realmente chama Close () internamente. Se estiver em um estado, pode.
Chris Marisic
2
Você está correto, é verdade. No entanto, eu deliberadamente não mencionei isso, pois não queria enganar ninguém a pensar que isso tem alguma coisa a ver com IDisposable ou o padrão associado. O fato de que esta implementação particular chama Close () é um detalhe da implementação, não o padrão.
Jeff Yates
3
A documentação do MSDN using também confirma esta resposta: a instrução using garante que Dispose seja chamado mesmo se ocorrer uma exceção enquanto você está chamando métodos no objeto. Você pode obter o mesmo resultado colocando o objeto dentro de um bloco try e chamando Dispose em um bloco finally; na verdade, é assim que a instrução using é traduzida pelo compilador.
banda larga de
20

É assim que o refletor decodifica o IL gerado pelo seu código:

private static void Main (string [] args)
{
    SqlConnection conn = new SqlConnection ("...");
    experimentar
    {
        conn.Open ();
        Fazer coisas();
    }
    finalmente
    {
        if (conn! = null)
        {
            conn.Dispose ();
        }
    }
}

Portanto, a resposta é sim, a conexão será fechada se

Fazer coisas()
lança uma exceção.

Florin Sabau
fonte
Adicione se conn.Open () lançar uma exceção. : D
Jeff Yates
Sim claro. Se tudo o que estiver no bloco APÓS a cláusula using lançar uma exceção, a conexão será fechada. A única maneira de o bloco finally não ser executado é se "new SqlConnection (...)" for acionado, mas nesse caso você não teria realmente uma conexão aberta válida para fechar. Então está bem.
Florin Sabau
-1

Dispose () não é chamado neste código.

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();
    }
}
Chade
fonte
Isso responde à pergunta do OP ??
Joey Phillips
Sim. A resposta é não. Dispose () não é chamado no código anexado. Além disso, a exceção que é lançada não é tratada e o programa explode.
Chad
Você deve estar olhando para o arquivo errado. "Dispose ()" é gravado em seu arquivo temporário. Ninguém afirma que um bloco de uso tratará uma exceção. Tente executar isso sem um depurador.
LarsTech
Eu executei exatamente esse mesmo código e ele chama Dispose (). Tem certeza de que sua resposta está correta?
Dnomyar96