O leitor de fluxo descartável fecha o fluxo?

166

Estou enviando um fluxo para métodos para escrever e, nesses métodos, estou usando um leitor / roteador binário. Quando o leitor / gravador é descartado, usingou apenas quando não é referenciado, o fluxo também é fechado?

Eu enviaria um BinaryReader / Writer, mas também estou usando um StreamReader (talvez eu deva contornar isso. Só estou usando isso para GetLine e ReadLine). Isso é bastante problemático se fechar o fluxo toda vez que um escritor / leitor for fechado.

Nefzen
fonte

Respostas:

204

Sim, StreamReader, StreamWriter, BinaryReadere BinaryWritermuito perto / eliminar as suas correntes subjacentes quando você chamar Disposesobre eles. Eles não descartam o fluxo se o leitor / gravador for apenas coletado no lixo - você deve sempre descartar o leitor / gravador, de preferência com uma usingdeclaração. (De fato, nenhuma dessas classes tem finalizadores, nem deveriam ter.)

Pessoalmente, prefiro ter uma declaração de uso também para o fluxo. Você pode aninhar usinginstruções sem chaves bem ordenadamente:

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

Mesmo que a usinginstrução para o fluxo seja um pouco redundante (a menos que o StreamReaderconstrutor gere uma exceção), considero a melhor prática como se você se livrar dele StreamReadere usar o fluxo diretamente em uma data posterior, você já terá a disposição correta semântica.

Jon Skeet
fonte
2
oh bom, isso só acontece quando chamamos Dispose, não quando supostamente finalizamos.
Nefzen 30/06/09
1
@Nefzen: Isso é porque não há garantia de que ordem seus objetos serão finalizados. Se o StreamReader e o Stream subjacente estiverem qualificados para finalização, o GC poderá finalizar o stream primeiro - o streamreader não terá uma referência ao stream. Por esse motivo, você só pode liberar recursos não gerenciados dentro de uma finalização (por exemplo, um FileStream fecha seu identificador de arquivo do Windows na finalização). Ah, e claro, se você nunca se desfazer, o fluxo ainda será coletado eventualmente (e o arquivo será fechado). É apenas uma prática muito ruim não descartar um fluxo.
JMarsch
13
Esse aninhamento faz com que o analisador de código VS se queixe: 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.isso deve ser ignorado? Até agora não recebi nenhuma exceção ...
HB
15
@ HB: É seguro ignorá-lo neste caso. Ou você pode simplesmente criar o fluxo na chamada do construtor para StreamReader. O aviso parece falso para mim, dado que os documentos IDisposable.Disposedeclaram explicitamente: "Se o método Dispose de um objeto for chamado mais de uma vez, o objeto deverá ignorar todas as chamadas após a primeira. O objeto não deve lançar uma exceção se o método Dispose for chamado várias vezes ".
Jon Skeet
5
@JonSkeet: Na verdade há uma página para este , você estava certo, isso é falso :)
HB
45

Essa é antiga, mas eu queria fazer algo parecido hoje e descobri que as coisas mudaram. Desde o .net 4.5, existe um leaveOpenargumento:

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

O único problema é que não é totalmente óbvio o que definir para os outros parâmetros. Aqui está uma ajuda:

Na página msdn do StreamReader Constructor (Stream):

Esse construtor inicializa a codificação para UTF8Encoding, a propriedade BaseStream usando o parâmetro stream e o tamanho do buffer interno para 1024 bytes.

Isso apenas deixa detectEncodingFromByteOrderMarksque, a julgar por código fonte, étrue

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

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

Seria bom se alguns desses padrões fossem expostos ou se os argumentos fossem opcionais para que pudéssemos especificar os que queremos.

acarlon
fonte
Informação muito boa! Nunca ouvi falar desse novo parâmetro e, na verdade, faz muito sentido.
precisa saber é
3
Para pessoas preguiçosas como eu, a resposta curta para deixar o fluxo aberto seria:using (var streamReader = new StreamReader(myStream, Encoding.UTF8, true, 1024, true))
beawolf
29

Sim. Você pode verificar isso observando a implementação com o 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
fonte
13

Seis anos atrasado, mas talvez isso possa ajudar alguém.

O StreamReader fecha a conexão quando é descartada. No entanto, "using (Stream stream = ...) {...}" com StreamReader / StreamWriter pode resultar no descarte do Stream duas vezes: (1) quando o objeto StreamReader é descartado (2) e quando o Stream usando o bloco fecha. Isso resulta em um aviso CA2202 ao executar a análise de código do VS.

Outra solução, tirada diretamente do CA2202 página , é usar um bloco try / finally. Se estiver configurado corretamente, isso fechará a conexão apenas uma vez.

Perto da parte inferior do CA2202 , a Microsoft recomenda o seguinte:

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

ao invés 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
fonte
2
O aviso também é discutido nos comentários da resposta aceita. Jon Skeet oferece alguns conselhos lá.
Marcin
Além disso, esteja ciente de que a instrução using é realmente convertida em um bloco try-finally pelo compilador.
27418 Jason Kelley
2

Sim. Chamar Dispose () e IDisposable (o que "usar" faz) deve fazer um objeto limpar todos os seus recursos. Isso inclui fluxos liberando e fechando seus descritores de arquivo.

Se, no seu caso, você deseja passá-lo para outros métodos, você precisa garantir que esses métodos não façam a leitura / gravação deles em um bloco de uso.

Joe M
fonte
-2

o fluxo descartado "usando" a palavra-chave ou chamando descarte explicitamente

Ahmed Said
fonte