2008-09-16 3 views
20

En C#, si quiero depurar de manera determinista los recursos no gestionados, puedo usar la palabra clave "using". Pero para los objetos dependientes múltiples, esto termina anidación más y más:¿Existe un mejor patrón de disposición determinista que el "uso" s anidado?

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 ser capaz de utilizar los destructores de hacerlo de esta manera:

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

¿Hay una mejor manera en C# para hacer esto? ¿O estoy atrapado con los múltiples niveles de anidación?

Respuesta

40

Usted no tiene que nido con múltiples usings:

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 
} 
+12

Eso está anidado: cada uso sucesivo se trata como un bloque de código de una línea; piense en una sentencia if, sans {}. Todo lo que ha ganado aquí es evitar presionar la tecla TAB. –

+1

Estoy de acuerdo con Greg Hurlman, es un truco de formateo: como si, mientras, etc, 'using' controla una sola instrucción, pero normalmente la usas con un bloque de instrucciones. –

+4

Bueno, sí, es un truco de formateo, pero el punto es que es un truco útil, y no obvio para alguien nuevo en C#. – Eclipse

-1

La instrucción using es el azúcar sintáctica que se convierte en:

Puede llama explícitamente a Dispose en sus objetos, pero no va a ser tan seguro, ya que si uno de ellos se produce una excepción, los recursos no será liberado apropiadamente

+0

NOTA: incorrecto. la inicialización del objeto se lleva a cabo FUERA del intento, incluso si hay más de un objeto. usar (X a = f(), b = f()) {g (a, b);} es lo mismo que X a = f(); X b = f(); intente con {g (a, b); } finally {b.Dispose(); a.Disponer(); hay una diferencia para la seguridad de excepción. – Aaron

1

En lugar de anidar utilizando sentencias, puede escribir las llamadas .Disponer manualmente, pero es casi seguro que perderá una en algún momento.

Ejecute FxCop u otra cosa que pueda asegurar que todas las instancias de tipo IDisposable implementables tengan una llamada .Dispose() o que se ocupen de la anidación.

8

Usted puede poner juntos usando declaraciones antes de la apertura se prepara 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

+0

Gracias por el enlace de referencia. – Sam

3

Usted podría utilizar 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 no usar {} para todos los bloques tiene sentido en mi humilde opinión.

0

se pueden 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 utilizar el intento normal por último enfoque:

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

Esto hace que para una red mucho más grande, más en líneas de código, sino una ganancia tangible en la legibilidad:

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

Dónde 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 un poco de esfuerzo, StreamWrapper se puede refactorizar para que sea más genérico y reutilizable.

1

he implementado soluciones como Michael Meadows 's antes, pero su código StreamWrapper no tiene en cuenta si los Dispose() métodos llamados de las variables miembro lanzan una excepción por una razón u otra, las posteriores Dispose() es no serán llamados y los recursos podrían colgar.La manera más segura para que uno a trabajar 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); 
     } 
    } 
+0

InnerException es una propiedad de solo lectura. ¿Cómo piensas en escribirlo? – supercat

0

Cabe señalar que, en general, cuando la creación de flujo con sede fuera otra corriente de la nueva corriente cerrará el que está siendo aprobada en tanto, 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 
} 
+2

NOTA: la excepción de seguridad falla. esto NO cerrará el FileStream si el constructor BufferedStream falla. usar sintaxis es realmente complicado comparado con C++ RAII. – Aaron

0

para este ejemplo vamos a suponer que usted tiene:

un archivo llamado 1.xml en c: \

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

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

No, no puede hacer eso: si BufferedStream o StreamReader lanzan una excepción durante la construcción, FileStream no se eliminará hasta que el GC se ponga a limpiar. No desea dejar los identificadores de archivos flotando así. – Eclipse

Cuestiones relacionadas