¿Se sigue llamando a Dispose cuando se lanza una excepción dentro de una declaración de uso?

103

En el siguiente ejemplo, ¿se cerrará y eliminará la conexión cuando se lance una excepción si está dentro de una usingdeclaración?

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

Sé que este código a continuación se asegurará de que lo haga, pero tengo curiosidad por saber cómo lo hace la declaración de uso.

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:

¿Cuál es la forma correcta de garantizar que una conexión SQL se cierre cuando se lanza una excepción?

Brian Kim
fuente

Respuestas:

112

Sí, usingenvuelve su código en un bloque de prueba / finalmente donde la finallyparte llamará Dispose()si existe. Sin embargo, no llamará Close()directamente, ya que solo verifica la IDisposableinterfaz que se está implementando y, por lo tanto, el Dispose()método.

Ver también:

Jeff Yates
fuente
5
Solo para señalar las clases de conexión, si refleja sobre ellas, verá que Dispose () de hecho llama internamente a Close (). Si está en un estado, puede.
Chris Marisic
2
Tienes razón, lo hace. Sin embargo, deliberadamente no lo mencioné, ya que no quería engañar a nadie para que pensara que esto tiene algo que ver con IDisposable o el patrón asociado. El hecho de que esta implementación particular llame a Close () es un detalle de la implementación, no del patrón.
Jeff Yates
3
La documentación de uso de MSDN también confirma esta respuesta: la declaración de uso garantiza que se llame a Dispose incluso si se produce una excepción mientras llama a métodos en el objeto. Puede lograr el mismo resultado colocando el objeto dentro de un bloque try y luego llamando a Dispose en un bloque finalmente; de hecho, así es como el compilador traduce la declaración using.
Banda ancha
20

Así es como reflector decodifica el IL generado por su código:

principal vacío estático privado (cadena [] argumentos)
{
    SqlConnection conn = new SqlConnection ("...");
    tratar
    {
        conn.Open ();
        Hacer cosas();
    }
    finalmente
    {
        si (conn! = nulo)
        {
            conn.Dispose ();
        }
    }
}

Entonces la respuesta es sí, cerrará la conexión si

Hacer cosas()
lanza una excepción.

Florin Sabau
fuente
Agregue si conn.Open () arroja una excepción. : D
Jeff Yates
Si seguro. Si lo que está en el bloque DESPUÉS de que la cláusula using arroja una excepción, la conexión se cerrará. La única forma en que el bloque finalmente no se ejecutará es si se lanza "new SqlConnection (...)", pero en ese caso no tendría una conexión abierta válida para cerrar. Entonces esta bien.
Florin Sabau
-1

Dispose () no se llama en este 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();
    }
}
Chad
fuente
¿Responde esto a la pregunta OP?
Joey Phillips
Si. La respuesta es no. Dispose () no se llama en el código adjunto. Además, la excepción que se lanza no se maneja y el programa explota.
Chad
Debe estar viendo el archivo incorrecto. "Dispose ()" se escribe en su archivo temporal. Nadie afirma que un using-block manejará una excepción. Intente ejecutar esto sin un depurador.
LarsTech
Ejecuté exactamente este mismo código y sí llama a Dispose (). ¿Estás seguro de que tu respuesta es correcta?
Dnomyar96