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

115

J'ai le code suivant

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

La dispose()méthode est appelée à la fin des usingaccolades, }non? Puisque j'ai returnavant la fin de la usingdéclaration, l' MemoryStreamobjet sera-t-il éliminé correctement? Que se passe t-il ici?

NLV
la source
4
@JonH: Trouvez le double exact, puis votez pour fermer dans ce cas s'il vous plaît.
Noldorin le
@Noldorin: Je suis allé à la recherche d'une dupe à ce sujet , parce que je pensais qu'il doit avoir été posée, mais je ne pouvais pas trouver un. Je suppose qu'il y a encore des questions faciles à poser. :)
Randolpho
@JonH et @Noldorin - des doublons auraient été présentés lors de la formation de la question, il recherche des «questions similaires», une fonctionnalité que les gens semblent ne pas utiliser suffisamment.
Adam Houldsworth
@Adam: allez essayer vous-même. Copiez / collez le titre et voyez quels doublons sont présentés par le système. Je vais vous donner un indice: la réponse est aucune. Idem si vous recherchez via Google ou la recherche de SO. Il semble que cette question n'ait pas été posée auparavant.
Randolpho
Aaap ... je reprends ça. Je viens de trouver un quasi-double, après quelques recherches très dédiées: stackoverflow.com/questions/2641692/ ... Maintenant, la question est posée complètement différemment, mais la question ultime est à peu près la même. Je suppose que nous pouvons considérer cela comme une dupe après tout.
Randolpho

Réponses:

167

Oui, Disposesera appelé. Il est appelé dès que l'exécution quitte la portée du usingbloc, quels que soient les moyens nécessaires pour quitter le bloc, que ce soit la fin de l'exécution du bloc, une returninstruction ou une exception.

Comme @Noldorin le souligne correctement, l'utilisation d'un usingbloc dans le code est compilée dans try/ finally, avec Disposeun appel dans le finallybloc. 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, parce qu'il finallyest garanti de s'exécuter après la tryfin de l'exécution du bloc, quel que soit son chemin d'exécution, il Disposeest garanti d'être appelé, quoi qu'il arrive.

Pour plus d'informations, consultez cet article MSDN .

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

Randolpho
la source
4
Je pense que vous constaterez qu'il est efficacement compilé dans un bloc try-finally avec un appel à Disposefinalement, donc cela fonctionne efficacement sur l'implémentation de finally, comme vous le décrivez.
Noldorin le
@Noldorin: exactement. Bien que je suppose que je pourrais être explicite à ce sujet. Edit à venir ....
Randolpho
1
Notez également qu'il existe certaines circonstances dans lesquelles l'exécution du bloc finally n'est pas garantie, comme l'utilisation de Environment.FailFast et si une exception StackOverFlowException se produit.
Christopher McAtackney
@ C.McAtackney: également un bon point. Aussi, IIRC, OutOfMemoryException; fondamentalement, si vous ne pouvez pas intercepter l'exception car il s'agit d'un échec d'exécution critique, Dispose ne sera pas appelé. Bien sûr, dans un tel cas, le programme est garanti de planter, avec toute la mémoire qui lui est allouée, donc dans 99,9% des cas, ce n'est pas un problème, à moins que vous ne fassiez des choses bancales comme écrire dans un fichier dans votre méthode de disposition. . Mis à part le crash catastrophique du programme, c'est.
Randolpho
Vous ne devez jamais utiliser l'instruction 'using ()' avec WCF - reportez-vous à cet article pour plus d'informations. Voici un extrait que j'utilise pour les proxies WCF: 'WCFProxy variableName = null; essayez {variableName = new WCFProxy (); // Code TODO ici variableName.Proxy.Close (); variableName.Dispose (); } catch (Exception) {if (variableName! = null && variableName.Proxy! = null) {variableName.Proxy.Abort (); } jeter; } '
Dave Black
18

usingLes instructions se comportent exactement comme des try ... finallyblocs, donc s'exécuteront toujours sur tous les chemins de sortie de code. Cependant, je crois qu'ils sont soumis aux très rares et rares situations dans lesquelles les finallyblocs 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 en dehors du GC sont mis en pause, ce qui signifie que les finallyblocs ne sont pas exécutés.

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

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

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

}

Et aussi délimité par des virgules (où les types sont les mêmes):

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

}
Adam Houldsworth
la source
4

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

Otávio Décio
la source
0

Jetez un œil à votre code dans Reflector après l'avoir compilé. Vous constaterez que le compilateur refactorise le code pour s'assurer que dispose est appelé sur le flux.

Wil P
la source