Existe-t-il un meilleur schéma d’élimination déterministe que les «utilisateurs» imbriqués?

StackOverflow https://stackoverflow.com/questions/75722

  •  09-06-2019
  •  | 
  •  

Question

En C #, si je souhaite nettoyer les ressources non gérées de manière déterministe, je peux utiliser l'option " using " mot-clé. Mais pour plusieurs objets dépendants, cela finit par imbriquer de plus en plus loin:

using (FileStream fs = new FileStream("c:\file.txt", FileMode.Open))
{
    using (BufferedStream bs = new BufferedStream(fs))
    {
        using (StreamReader sr = new StreamReader(bs))
        {
            // use sr, and have everything cleaned up when done.
        }
    }
}

En C ++, je suis habitué à pouvoir utiliser des destructeurs pour le faire comme suit:

{    
    FileStream fs("c:\file.txt", FileMode.Open);
    BufferedStream bs(fs);
    StreamReader sr(bs);
    // use sr, and have everything cleaned up when done.
}

Existe-t-il une meilleure façon de faire cela en C #? Ou suis-je coincé avec les multiples niveaux d'imbrication?

Était-ce utile?

La solution

Vous n'avez pas besoin d'imbriquer avec plusieurs utilisations:

using (FileStream fs = new FileStream("c:\file.txt", FileMode.Open))
using (BufferedStream bs = new BufferedStream(fs))
using (StreamReader sr = new StreamReader(bs))
{
    // all three get disposed when you're done
}

Autres conseils

Vous pouvez regrouper des instructions avant les accolades d'ouverture de la manière suivante:

  using (StreamWriter w1 = File.CreateText("W1"))
  using (StreamWriter w2 = File.CreateText("W2"))
  {
      // code here
  }

http://blogs.msdn.com/ericgu /archive/2004/08/05/209267.aspx

Vous pouvez utiliser cette syntaxe pour condenser un peu les choses:

using (FileStream fs = new FileStream("c:\file.txt", FileMode.Open))
using (BufferedStream bs = new BufferedStream(fs))
using (StreamReader sr = new StreamReader(bs))
{
}

C’est l’une des rares occasions où ne pas utiliser {} pour tous les blocs est logique à mon humble avis.

Au lieu d'imbriquer des instructions, vous pouvez simplement écrire les appels .Dispose manuellement - mais vous en manquerez certainement un à un moment donné.

Lancez FxCop ou un autre outil permettant de vous assurer que toutes les instances de type implémentant IDisposable ont un appel .Dispose () ou gérez l'imbrication.

J'ai mis en œuvre des solutions telles que Michael Meadows " auparavant, mais son code StreamWrapper ne prend pas en compte si les méthodes Dispose () appelées sur les variables membres lève une exception pour une raison ou une autre, les codes Dispose () suivants ne seront pas appelés et les ressources risquent de pendre. Le moyen le plus sûr de travailler est le suivant:

        var exceptions = new List<Exception>();

        try
        {
            this.sr.Dispose();
        }
        catch (Exception ex)
        {
            exceptions.Add(ex);
        }

        try
        {
            this.bs.Dispose();
        }
        catch (Exception ex)
        {
            exceptions.Add(ex);
        }

        try
        {
            this.fs.Dispose();
        }
        catch (Exception ex)
        {
            exceptions.Add(ex);
        }

        if (exceptions.Count > 0)
        {
            throw new AggregateException(exceptions);
        }
    }

vous pouvez omettre les accolades, comme:

using (FileStream fs = new FileStream("c:\file.txt", FileMode.Open))
using (BufferedStream bs = new BufferedStream(fs))
using (StreamReader sr = new StreamReader(bs))
{
        // use sr, and have everything cleaned up when done.
}

ou utilisez l'approche habituelle d'essayer enfin:

FileStream fs = new FileStream("c:\file.txt", FileMode.Open);
BufferedStream bs = new BufferedStream(fs);
StreamReader sr = new StreamReader(bs);
try
{
        // use sr, and have everything cleaned up when done.
}finally{
   sr.Close(); // should be enough since you hand control to the reader
}

Cela donne un net plus beaucoup plus grand en lignes de code, mais un gain tangible en lisibilité:

using (StreamWrapper wrapper = new StreamWrapper("c:\file.txt", FileMode.Open))
{
    // do stuff using wrapper.Reader
}

Où StreamWrapper est défini ici:

private class StreamWrapper : IDisposable
{
    private readonly FileStream fs;
    private readonly BufferedStream bs;
    private readonly StreamReader sr;

    public StreamWrapper(string fileName, FileMode mode)
    {
        fs = new FileStream(fileName, mode);
        bs = new BufferedStream(fs);
        sr = new StreamReader(bs);
    }

    public StreamReader Reader
    {
        get { return sr; }
    }

    public void Dispose()
    {
        sr.Dispose();
        bs.Dispose();
        fs.Dispose();
    }
}

Avec quelques efforts, StreamWrapper pourrait être remanié pour devenir plus générique et réutilisable.

Il convient de noter que généralement, lors de la création d'un flux basé sur un autre flux, le nouveau flux ferme celui qui est transmis. Donc, pour réduire davantage votre exemple:

using (Stream Reader sr = new StreamReader( new BufferedStream( new FileStream("c:\file.txt", FileMode.Open))))
{
    // all three get disposed when you're done
}

pour cet exemple, supposons que vous avez:

un fichier nommé 1.xml sous c: \

une zone de texte nommée textBox1, avec les propriétés multilignes définies sur ON.

const string fname = @"c:\1.xml";

StreamReader sr=new StreamReader(new BufferedStream(new FileStream(fname,FileMode.Open,FileAccess.Read,FileShare.Delete)));
textBox1.Text = sr.ReadToEnd();

L'instruction using est un sucre syntaxique qui se transforme en:

   try
   {
      obj declaration
      ...
   }
   finally
   {
      obj.Dispose();
   }

Vous pouvez appeler explicitement Dispose sur vos objets, mais cela ne sera pas aussi sûr, car si l'un d'eux lève une exception, les ressources ne seront pas libérées correctement.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top