¿Existe un patrón de eliminación determinista mejor que el de "uso" anidado?

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

  •  09-06-2019
  •  | 
  •  

Pregunta

En C#, si quiero limpiar de manera determinista los recursos no administrados, puedo usar la palabra clave "using".Pero para múltiples objetos dependientes, esto termina anidándose cada vez más:

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++, estoy acostumbrado a poder usar destructores para hacerlo así:

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

¿Existe una mejor manera en C# de hacer esto?¿O estoy atrapado en los múltiples niveles de anidamiento?

¿Fue útil?

Solución

No es necesario anidar con múltiples usos:

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
}

Otros consejos

Puedes juntar declaraciones de uso antes de las llaves de apertura de esta manera:

  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

Podrías usar esta sintaxis para condensar un poco las cosas:

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

Esta es una de esas raras ocasiones en las que, en mi humilde opinión, no usar { } para todos los bloques tiene sentido.

En lugar de anidar declaraciones de uso, puede simplemente escribir las llamadas .Dispose manualmente, pero es casi seguro que se perderá alguna en algún momento.

Ejecute FxCop o algo más que pueda garantizar que todas las instancias de tipo que implementan IDisposable tengan una llamada .Dispose() o se ocupe del anidamiento.

He implementado soluciones como Michael Pradoses antes, pero su StreamWrapper El código no tiene en cuenta si el Dispose() Los métodos llamados en las variables miembro arrojan una excepción por una razón u otra, las siguientes Dispose()es no será convocado y los recursos podrían quedar colgados.La forma más segura de que funcione es:

        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);
        }
    }

puedes omitir las llaves, como:

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

o utilice el método de intento final habitual:

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
}

Esto genera una ventaja neta mucho mayor en líneas de código, pero una ganancia tangible en legibilidad:

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

Donde StreamWrapper se define aquí:

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();
    }
}

Con algo de esfuerzo, StreamWrapper podría refactorizarse para que sea más genérico y reutilizable.

Cabe señalar que, en general, cuando se crea una secuencia basada en otra secuencia, la nueva secuencia cerrará la que se está pasando.Entonces, para reducir aún más su ejemplo:

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

Para este ejemplo supongamos que tiene:

un archivo llamado 1.xml en c:\

un cuadro de texto llamado textBox1, con las propiedades de varias líneas activadas.

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();

La declaración de uso es azúcar sintáctica que se convierte en:

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

Puedes llamar explícitamente a Dispose en tus objetos, pero no será tan seguro, ya que si uno de ellos genera una excepción, los recursos no se liberarán correctamente.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top