web-dev-qa-db-fra.com

Devrais-je appeler Close () ou Dispose () pour les objets de flux?

Des classes telles que Stream, StreamReader, StreamWriter etc implémentent l'interface IDisposable. Cela signifie que nous pouvons appeler la méthode Dispose() sur les objets de ces classes. Ils ont également défini une méthode public appelée Close(). Maintenant, cela me confond, quant à ce que devrais-je appeler une fois que je suis fait avec des objets? 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 les constructions using(), qui appellent automatiquement la méthode Dispose() sur chaque objet. Mais j'appelle aussi les méthodes Close(). Est ce bien?

Suggérez-moi les meilleures pratiques lors de l’utilisation d’objets flux. :-)

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

Est-ce bien?

138
Nawaz

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 de 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.

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

Il faut donc savoir s'il est plus facile de lire Dispose(), Close() et/ou using ( ... ) { ... }.

Ma préférence personnelle est que using ( ... ) { ... } devrait toujours être utilisé lorsque cela est possible car cela vous aide à "ne pas utiliser de ciseaux".

Mais, bien que cela aide la correction, cela réduit la lisibilité. En C #, nous avons déjà une pléthore d’accolades fermantes. Comment savons-nous laquelle effectue réellement la fermeture du flux?

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é.

96
Enigmativity

Non, vous ne devriez pas appeler ces méthodes manuellement. À la fin du bloc using, la méthode Dispose est automatiquement appelée, ce qui permet de libérer les ressources non gérées (au moins pour les classes .NET BCL standard telles que les flux, les lecteurs/rédacteurs, ...). 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.

45
Darin Dimitrov

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 true.

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

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 m'en tiendrai à la première option, car elle contient moins de "bruit".

12
Heinzi

Sur de nombreuses classes prenant en charge les méthodes Close et Dispose, les deux appels sont équivalents. Sur certaines classes, cependant, il est possible de rouvrir un objet qui a été fermé. Certaines de ces classes peuvent maintenir certaines ressources en vie après une fermeture afin de permettre leur réouverture; d'autres peuvent ne 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'appel sur un objet qui ne sera plus jamais utilisé soit au pire inoffensif. Je recommande donc d'appeler soit IDisposable.Dispose, soit une méthode appelée Dispose sur chaque objet IDisposable, qu'il s'agisse ou non d'un objet. appelle Fermer.

4
supercat