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?
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.
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))
{
}
Votre objet MemoryStream sera éliminé correctement, pas besoin de vous en soucier.
Avec l'instruction using
, l'objet sera supprimé quel que soit le chemin d'achèvement.
Lectures complémentaires ...
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.