¿Eliminar el lector de flujo cierra el flujo?

166

Estoy enviando una secuencia a métodos para escribir, y en esos métodos estoy usando un lector / wrtier binario. Cuando el lector / escritor se deshace, ya sea porusing o solo cuando no está referenciado, ¿también se cierra la secuencia?

Enviaría un BinaryReader / Writer, pero también estoy usando un StreamReader (tal vez debería evitar eso. Solo lo estoy usando para GetLine y ReadLine). Esto es bastante problemático si cierra la transmisión cada vez que se cierra un escritor / lector.

Nefzen
fuente

Respuestas:

204

Sí, StreamReader, StreamWriter, BinaryReadery BinaryWritertodo está cerca / enajenar sus corrientes subyacentes cuando se llama Disposeen ellos. Sin embargo, no eliminan la transmisión si el lector / escritor es solo basura recolectada; siempre debe deshacerse del lector / escritor, preferiblemente con una usingdeclaración. (De hecho, ninguna de estas clases tiene finalizadores, ni deberían tenerlos).

Personalmente, prefiero tener una declaración de uso para la transmisión también. Puede anidar usingdeclaraciones sin llaves de forma bastante clara:

using (Stream stream = ...)
using (StreamReader reader = new StreamReader(stream, Encoding.Whatever))
{
}

A pesar de que la usingdeclaración de la transmisión es algo redundante (a menos que el StreamReaderconstructor arroje una excepción), considero que es una buena práctica, ya que si se deshace de StreamReaderla transmisión y solo la usa directamente en una fecha posterior, ya tendrá la disposición correcta semántica.

Jon Skeet
fuente
2
oh bueno, solo sucede cuando se llama Dispose, no cuando se supone que finaliza.
Nefzen
1
@Nefzen: Eso es porque no hay garantía de qué orden se finalizarán sus objetos. Si tanto el StreamReader como el Stream subyacente son elegibles para la finalización, el GC podría finalizar el flujo primero; luego, el lector de flujo no tendría una referencia para transmitir. Por esta razón, solo puede liberar recursos no administrados dentro de una finalización (por ejemplo, un FileStream cierra su identificador de archivo de Windows en su finalización). Ah, y por supuesto, si nunca se deshace, la transmisión se recopilará eventualmente (y el archivo se cerrará). Es una muy mala práctica no disponer de una secuencia.
JMarsch
13
Este anidamiento hace que el analizador de código VS se queje: CA2202 : Microsoft.Usage : Object 'stream' can be disposed more than once in method '...'. To avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object.¿Debería ignorarse eso? No obtuve ninguna excepción hasta ahora ...
HB
15
@HB: es seguro ignorarlo en este caso. O simplemente puede crear la secuencia en la llamada al constructor StreamReader. La advertencia me parece falsa, dado que los documentos IDisposable.Disposeindican explícitamente: "Si se llama al método Dispose de un objeto más de una vez, el objeto debe ignorar todas las llamadas después de la primera. El objeto no debe lanzar una excepción si su método Dispose es llamado varias veces ".
Jon Skeet
55
@ JonSkeet: En realidad hay una página para esto , estabas en lo cierto, esto es falso :)
HB
45

Este es viejo, pero quería hacer algo similar hoy y descubrí que las cosas han cambiado. Desde .net 4.5, hay un leaveOpenargumento:

public StreamReader( Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize, bool leaveOpen )

El único problema es que no es del todo obvio qué establecer para los otros parámetros. Aquí hay algo de ayuda:

Desde la página msdn para StreamReader Constructor (Stream):

Este constructor inicializa la codificación a UTF8Encoding, la propiedad BaseStream utilizando el parámetro de flujo y el tamaño del búfer interno a 1024 bytes.

Eso solo deja detectEncodingFromByteOrderMarksque a juzgar por el código fuente estrue

public StreamReader(Stream stream)
        : this(stream, true) {
}

public StreamReader(Stream stream, bool detectEncodingFromByteOrderMarks)
        : this(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks, DefaultBufferSize) {
}

Sería bueno si algunos de esos valores predeterminados estuvieran expuestos o si los argumentos fueran opcionales para que pudiéramos especificar los que queremos.

acarlon
fuente
Muy buena información! Nunca he oído hablar de este nuevo parámetro y en realidad tiene mucho sentido.
julealgon
3
Para la gente perezosa como yo, la respuesta corta para dejar la transmisión abierta sería:using (var streamReader = new StreamReader(myStream, Encoding.UTF8, true, 1024, true))
beawolf
29

Sí lo hace Puede verificar esto mirando la implementación con Reflector.

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;    
            this.encoding = null;
            this.decoder = null;
            this.byteBuffer = null;
            this.charBuffer = null;
            this.charPos = 0;
            this.charLen = 0;
            base.Dispose(disposing);
        }
    }
}
Brian Rasmussen
fuente
13

Seis años tarde pero tal vez esto pueda ayudar a alguien.

StreamReader cierra la conexión cuando se elimina. Sin embargo, "usar (Stream stream = ...) {...}" con StreamReader / StreamWriter puede hacer que Stream se elimine dos veces: (1) cuando el objeto StreamReader está dispuesto (2) y cuando Stream usa el bloque cierra Esto da como resultado una advertencia CA2202 cuando se ejecuta el análisis de código de VS.

Otra solución, tomada directamente del CA2202 página , es usar un bloque try / finally. Configuración correcta, esto solo cerrará la conexión una vez.

Cerca del final de CA2202 , Microsoft recomienda usar lo siguiente:

Stream stream = null;
try
{
    stream = new FileStream("file.txt", FileMode.OpenOrCreate);
    using (StreamWriter writer = new StreamWriter(stream))
    {
        stream = null;
        // Use the writer object...
    }
}
finally
{
    if(stream != null)
        stream.Dispose();
}

en vez de...

// Generates a CA2202 warning
using (Stream stream = new FileStream("file.txt", FileMode.Open))
using (XmlReader reader = new XmlReader (stream))
{
    // Use the reader object...
}
Sunsetquest
fuente
2
La advertencia también se discute en los comentarios de la respuesta aceptada. Jon Skeet ofrece algunos consejos allí.
Marcin
Además, tenga en cuenta que el compilador convierte la declaración de uso en un bloque try-finally.
Jason Kelley
2

Si. Llamar a Dispose () on e IDisposable (que "usar" hace) debería hacer que un objeto limpie todos sus recursos. Esto incluye flujos que descargan y cierran sus descriptores de archivo.

Si, en su caso, desea pasarlo a otros métodos, debe asegurarse de que esos métodos no hagan su lectura / escritura en un bloque de uso.

Joe M
fuente
-2

la secuencia eliminada ya sea "usando" la palabra clave o llamando a eliminar explícitamente

Ahmed dijo
fuente