web-dev-qa-db-fra.com

Que se passe-t-il si je reviens avant la fin de la déclaration d'utilisation? La disposition sera-t-elle appelée?

J'ai le code suivant

using(MemoryStream ms = new MemoryStream())
{
     //code
     return 0;
}

La méthode dispose() est appelée à la fin de l'instruction using accolades } Non? Puisque je return avant la fin de l'instruction using, l'objet MemoryStream sera-t-il correctement supprimé? Que se passe t-il ici?

109
NLV

Oui, Dispose sera appelé. Il est appelé dès que l'exécution quitte la portée du bloc using, quel que soit le moyen qu'il a fallu pour quitter le bloc, que ce soit la fin de l'exécution du bloc, une instruction return, ou une exception.

Comme @Noldorin le souligne correctement, l'utilisation d'un bloc using dans le code est compilée dans try/finally, avec Dispose étant appelé dans le finally bloquer. Par exemple, le code suivant:

using(MemoryStream ms = new MemoryStream())
{
     //code
     return 0;
}

devient effectivement:

MemoryStream ms = new MemoryStream();
try
{
    // code
    return 0;
}
finally
{
    ms.Dispose();
}

Ainsi, comme finally est garanti pour s'exécuter une fois l'exécution du bloc try terminée, quel que soit son chemin d'exécution, Dispose est garanti d'être appelé, quoi qu'il arrive.

Pour plus d'informations, consultez cet article MSDN .

Addendum:
Juste une petite mise en garde à ajouter: comme Dispose est garanti d'être appelé, c'est presque toujours une bonne idée de s'assurer que Dispose ne lève jamais d'exception lorsque vous implémentez IDisposable. Malheureusement, il y a des classes dans la bibliothèque principale que jette dans certaines circonstances lorsque Dispose est appelée - je regarde vous, WCF Service Reference/Client Proxy! - et lorsque cela se produit, il peut être très difficile de retrouver l'exception d'origine si Dispose a été appelé lors d'un déroulement de pile d'exceptions, car l'exception d'origine est avalée en faveur de la nouvelle exception générée par Dispose appel. Cela peut être extrêmement frustrant. Ou est-ce exaspérant et frustrant? Un des deux. Peut-être les deux.

160
Randolpho

using les instructions se comportent exactement comme try ... finally blocs, s'exécutera donc toujours sur tous les chemins de sortie de code. Cependant, je pense qu'ils sont soumis aux très rares et rares situations dans lesquelles les blocs finally ne sont pas appelés. Un exemple dont je me souviens est si le thread de premier plan se termine alors que les threads d'arrière-plan sont actifs: tous les threads à l'exception du GC sont suspendus, ce qui signifie que les blocs finally ne sont pas exécutés.

Édition évidente: ils se comportent de la même manière en dehors de la logique qui leur permet de gérer des objets IDisposables, d'oh.

Contenu bonus: ils peuvent être empilés (où les types diffèrent):

using (SqlConnection conn = new SqlConnection("string"))
using (SqlCommand comm = new SqlCommand("", conn))
{

}

Et également séparés par des virgules (où les types sont les mêmes):

using (SqlCommand comm = new SqlCommand("", conn), 
       SqlCommand comm2 = new SqlCommand("", conn))
{

}
18
Adam Houldsworth

Votre objet MemoryStream sera éliminé correctement, pas besoin de vous en soucier.

4
Otávio Décio

Avec l'instruction using, l'objet sera supprimé quel que soit le chemin d'achèvement.

Lectures complémentaires ...

3
RSolberg

Jetez un œil à votre code dans le réflecteur après l'avoir compilé. Vous constaterez que le compilateur refactorise le code pour garantir que l'élimination est appelée sur le flux.

0
Wil P