c# - recursos - ¿Debo llamar a Close() o Dispose() para los objetos de transmisión?
recursos no administrados (4)
Las clases como Stream
, StreamReader
, StreamWriter
, etc. implementan IDisposable
interfaz IDisposable
. Eso significa que podemos llamar al método Dispose()
en los objetos de estas clases. También definieron un método public
llamado Close()
. Ahora que me confunde, ¿a qué debo llamar una vez que termine con los objetos? ¿Qué pasa si llamo a ambos?
Mi código actual es este:
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();
}
}
Como ve, escribí constructos using()
, que llaman automáticamente al método Dispose()
en cada objeto. Pero también llamo a métodos Close()
. ¿Es correcto?
Por favor, sugiera las mejores prácticas al usar objetos de transmisión. :-)
El ejemplo de MSDN no usa construcciones using()
y llama al método Close()
:
¿Esta bien?
En muchas clases que admiten métodos Close y Dispose, las dos llamadas serían equivalentes. En algunas clases, sin embargo, es posible volver a abrir un objeto que ha sido Close''d. Algunas de estas clases pueden mantener vivos algunos recursos después de un Cierre, para permitir la reapertura; otros pueden no mantener recursos vivos en Cerrar, pero pueden establecer un indicador en Eliminar para prohibir explícitamente la reapertura.
El contrato para IDisposable.Dispose requiere explícitamente que llamarlo a un objeto que nunca se volverá a utilizar será, en el peor de los casos, inofensivo, por lo que recomendaría llamar IDisposable.Dispose o un método llamado Dispose en cada objeto IDisposable, ya sea que llama Cerrar.
La documentación dice que estos dos métodos son equivalentes:
StreamReader.Close : esta implementación de Close llama al método Dispose que pasa un valor verdadero.
StreamWriter.Close : esta implementación de Close llama al método Dispose que pasa un valor verdadero.
Stream.Close : este método llama a Dispose, especificando true para liberar todos los recursos.
Entonces, ambos son igualmente válidos:
/* Option 1 */
using (StreamWriter writer = new StreamWriter(filename)) {
// do something
}
/* Option 2 */
StreamWriter writer = new StreamWriter(filename)
try {
// do something
}
finally {
writer.Close();
}
Personalmente, me quedaría con la primera opción, ya que contiene menos "ruido".
No, no deberías llamar esos métodos manualmente. Al final del bloque de using
, se llama automáticamente al método Dispose, que se ocupará de liberar recursos no administrados (al menos para las clases .NET BCL estándar tales como secuencias, lectores / escritores, ...). Entonces también puedes escribir tu código así:
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);
}
}
}
El método Close llama a Dispose.
Un salto rápido a Reflector.NET muestra que el método Close()
en StreamWriter
es:
public override void Close()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
Y StreamReader
es:
public override void Close()
{
this.Dispose(true);
}
La anulación de Dispose(bool disposing)
en StreamReader
es:
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);
}
}
}
El método StreamWriter
es similar.
Entonces, al leer el código, está claro que puede llamar a Close()
y a Dispose()
en las transmisiones con la frecuencia que desee y en cualquier orden. No cambiará el comportamiento de ninguna manera.
De modo que todo se reduce a si es o no más fácil usar Dispose()
, Close()
y / o using ( ... ) { ... }
.
Mi preferencia personal es que using ( ... ) { ... }
siempre que sea posible, ya que le ayuda a "no usar tijeras".
Pero, aunque esto ayuda a la corrección, sí reduce la legibilidad. En C #, ya tenemos una plétora de llaves de cierre, entonces, ¿cómo sabemos cuál de las dos realiza el cierre en la transmisión?
Entonces creo que es mejor hacer esto:
using (var stream = ...)
{
/* code */
stream.Close();
}
No afecta el comportamiento del código, pero ayuda a la legibilidad.