Dispose est-il toujours appelé lorsqu'une exception est levée à l'intérieur d'une instruction using?

103

Dans l'exemple ci-dessous, la connexion va-t-elle se fermer et être supprimée lorsqu'une exception est levée si elle se trouve dans une usinginstruction?

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

Je sais que ce code ci-dessous s'assurera que c'est le cas, mais je suis curieux de savoir comment utiliser l'instruction le fait.

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

En relation:

Quelle est la bonne façon de garantir qu'une connexion SQL est fermée lorsqu'une exception est levée?

Brian Kim
la source

Réponses:

112

Oui, usingencapsule votre code dans un bloc try / finally où la finallypartie sera appelée Dispose()si elle existe. Cependant, il n'appellera pas Close()directement car il ne vérifie que l' IDisposableinterface implémentée et donc la Dispose()méthode.

Voir également:

Jeff Yates
la source
5
Juste pour signaler les classes de connexion si vous réfléchissez dessus, vous verrez que Dispose () appelle effectivement Close (). Si c'est dans un état, c'est possible.
Chris Marisic le
2
Vous avez raison, c'est vrai. Cependant, je ne l'ai délibérément pas mentionné car je ne voulais induire personne en erreur en pensant que cela a quelque chose à voir avec IDisposable ou le modèle associé. Le fait que cette implémentation particulière appelle Close () est un détail de l'implémentation, pas du modèle.
Jeff Yates le
3
MSDN using documentation confirme également cette réponse: l' instruction using garantit que Dispose est appelé même si une exception se produit pendant que vous appelez des méthodes sur l'objet. Vous pouvez obtenir le même résultat en plaçant l'objet dans un bloc try, puis en appelant Dispose dans un bloc finally; en fait, c'est ainsi que l'instruction using est traduite par le compilateur.
haut débit
20

Voici comment réflecteur décode l'IL généré par votre code:

private static void Main (string [] args)
{
    SqlConnection conn = new SqlConnection ("...");
    essayer
    {
        conn.Open ();
        Faire des trucs();
    }
    enfin
    {
        si (conn! = null)
        {
            conn.Dispose ();
        }
    }
}

Donc la réponse est oui, cela fermera la connexion si

Faire des trucs()
lève une exception.

Florin Sabau
la source
Ajouter si conn.Open () lève une exception. : D
Jeff Yates
Oui bien sûr. Si tout ce qui se trouve dans le bloc APRÈS la clause using lève une exception, la connexion sera fermée. La seule façon dont le bloc finally ne sera pas exécuté est si le "new SqlConnection (...)" lève, mais dans ce cas, vous n'auriez pas réellement une connexion ouverte valide à fermer. Alors ça va.
Florin Sabau
-1

Dispose () n'est pas appelé dans ce code.

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();
    }
}
Tchad
la source
Cela répond-il à la question du PO ??
Joey Phillips
Oui. La réponse est non. Dispose () n'est pas appelé dans le code joint. De plus, l'exception qui est lancée n'est pas gérée et le programme explose.
Tchad
Vous devez regarder le mauvais fichier. "Dispose ()" est écrit dans votre fichier temporaire. Personne ne prétend qu'un bloc d'utilisation gérera une exception. Essayez de l'exécuter sans débogueur.
LarsTech
J'ai exécuté exactement le même code et il appelle Dispose (). Etes-vous sûr que votre réponse est correcte?
Dnomyar96 le