Dois-je appeler Close () ou Dispose () pour les objects de stream?

Des classes telles que Stream , StreamWriter , StreamWriter etc. implémentent une interface IDisposable . Cela signifie que nous pouvons appeler la méthode Dispose() sur les objects de ces classes. Ils ont également défini une méthode public appelée Close() . Maintenant, cela me déconcerte, que dois-je appeler une fois que je suis fait avec des objects? Et si j’appelle les deux?

Mon code actuel est le suivant:

 using (Stream responseStream = response.GetResponseStream()) { using (StreamReader reader = new StreamReader(responseStream)) { using (StreamWriter writer = new StreamWriter(filename)) { int chunkSize = 1024; while (!reader.EndOfStream) { char[] buffer = new char[chunkSize]; int count = reader.Read(buffer, 0, chunkSize); if (count != 0) { writer.Write(buffer, 0, count); } } writer.Close(); } reader.Close(); } } 

Comme vous le voyez, j’ai écrit des constructions using() , qui appellent automatiquement la méthode Dispose() sur chaque object. Mais j’appelle aussi les méthodes Close() . Est ce bien?

Veuillez me suggérer les meilleures pratiques lors de l’utilisation d’objects de stream. 🙂

L’exemple MSDN n’utilise pas les constructions using() et appelle la méthode Close() :

  • Comment: télécharger des fichiers avec FTP

Est-ce bien?

Un saut rapide dans Reflector.NET montre que la méthode Close() sur StreamWriter est:

 public override void Close() { this.Dispose(true); GC.SuppressFinalize(this); } 

Et StreamReader est:

 public override void Close() { this.Dispose(true); } 

La substitution Dispose(bool disposing) dans StreamReader est:

 protected override void Dispose(bool disposing) { try { if ((this.Closable && disposing) && (this.stream != null)) { this.stream.Close(); } } finally { if (this.Closable && (this.stream != null)) { this.stream = null; /* deleted for brevity */ base.Dispose(disposing); } } } 

La méthode StreamWriter est similaire.

Donc, en lisant le code, il est clair que vous pouvez appeler Close() & Dispose() sur les stream aussi souvent que vous le souhaitez et dans n’importe quel ordre. Cela ne changera en rien le comportement.

Il s’agit donc de savoir s’il est plus lisible d’utiliser Dispose() , Close() et / ou d’ using ( ... ) { ... } .

Ma préférence personnelle est que l’ using ( ... ) { ... } devrait toujours être utilisée dans la mesure du possible car elle vous aide à “ne pas exécuter avec des ciseaux”.

Mais, bien que cela aide la correction, elle réduit la lisibilité. En C #, nous avons déjà de nombreuses accolades fermantes alors comment pouvons-nous savoir lequel effectue la fermeture sur le stream?

Donc, je pense qu’il est préférable de faire ceci:

 using (var stream = ...) { /* code */ stream.Close(); } 

Cela n’affecte pas le comportement du code, mais cela facilite la lisibilité.

Non, vous ne devriez pas appeler ces méthodes manuellement. A la fin du bloc using , la méthode Dispose est appelée automatiquement, ce qui prend soin de libérer les ressources non managées (au moins pour les classes .NET BCL standard telles que les stream, les lecteurs / écrivains, …). Donc, vous pouvez aussi écrire votre code comme ceci:

 using (Stream responseStream = response.GetResponseStream()) using (StreamReader reader = new StreamReader(responseStream)) using (StreamWriter writer = new StreamWriter(filename)) { int chunkSize = 1024; while (!reader.EndOfStream) { char[] buffer = new char[chunkSize]; int count = reader.Read(buffer, 0, chunkSize); if (count != 0) { writer.Write(buffer, 0, count); } } } 

La méthode Close appelle Dispose.

La documentation indique que ces deux méthodes sont équivalentes:

StreamReader.Close : Cette implémentation de Close appelle la méthode Dispose en transmettant une valeur vraie.

StreamWriter.Close : Cette implémentation de Close appelle la méthode Dispose en transmettant une valeur vraie.

Stream.Close : cette méthode appelle Dispose, en spécifiant true pour libérer toutes les ressources.

Donc, les deux sont également valables:

 /* Option 1 */ using (StreamWriter writer = new StreamWriter(filename)) { // do something } /* Option 2 */ StreamWriter writer = new StreamWriter(filename) try { // do something } finally { writer.Close(); } 

Personnellement, je restrais avec la première option, car elle contient moins de “bruit”.

Sur de nombreuses classes prenant en charge les méthodes Close et Dispose, les deux appels seraient équivalents. Sur certaines classes, cependant, il est possible de ré-ouvrir un object qui a été fermé. Certaines de ces classes peuvent garder certaines ressources en vie après une fermeture, afin de permettre la réouverture; les autres ne peuvent garder aucune ressource en vie lors de la fermeture, mais peuvent définir un indicateur sur Dispose pour interdire explicitement la réouverture.

Le contrat pour IDisposable.Dispose exige explicitement que l’appeler sur un object qui ne sera plus jamais utilisé sera au pire inoffensif, alors je vous recommande d’appeler soit IDisposable.Dispose, soit une méthode appelée Dispose sur chaque object IDisposable, même si appelle Close.