L'eliminazione dello streamreader chiude lo stream?


166

Sto inviando un flusso a metodi su cui scrivere e in quei metodi sto usando un lettore / scrittore binario. Quando il lettore / scrittore viene eliminato, da usingo solo quando non è referenziato, anche il flusso viene chiuso ??

Vorrei inviare un BinaryReader / Writer, ma sto usando anche uno StreamReader (forse dovrei aggirarlo. Sto solo usando quello per GetLine e ReadLine). Questo è abbastanza problematico se chiude il flusso ogni volta che uno scrittore / lettore viene chiuso.

Risposte:


204

Sì, StreamReader, StreamWriter, BinaryReadere BinaryWritertutti nelle vicinanze / smaltire i loro flussi sottostanti quando si chiama Disposesu di loro. Essi non dispongono del flusso se il lettore / scrittore è solo spazzatura raccolte anche se - si dovrebbe sempre Smaltire il lettore / scrittore, preferibilmente con una usingdichiarazione. (In effetti, nessuna di queste classi ha finalizzatori, né dovrebbero averli.)

Personalmente preferisco avere una dichiarazione using anche per lo stream. Puoi annidare le usingistruzioni senza parentesi graffe abbastanza ordinatamente:

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

Anche se l' usingistruzione per lo stream è in qualche modo ridondante (a meno che il StreamReadercostruttore non generi un'eccezione) la considero una buona pratica, quindi se ti sbarazzi del StreamReadere usi lo stream direttamente in un secondo momento, avrai già la giusta disposizione semantica.


2
oh bene, succede solo quando si chiama Dispose, non quando si presume che si stia finalizzando.
Nefzen,

1
@Nefzen: Questo perché non esiste alcuna garanzia su quale ordine verranno finalizzati i tuoi oggetti. Se sia StreamReader che Stream sottostanti sono idonei per la finalizzazione, il GC potrebbe finalizzare prima lo stream, quindi lo streamreader non avrebbe un riferimento allo stream. Per questo motivo, è possibile rilasciare solo risorse non gestite all'interno di una finalizzazione (ad esempio, un FileStream chiude il suo handle di file di Windows nella sua finalizzazione). Oh, e ovviamente, se non lo fai mai, il flusso verrà comunque raccolto alla fine (e il file chiuso). È solo una brutta pratica non smaltire un flusso.
JMarsch,

13
Questo annidamento provoca il lamento dell'analizzatore di codice VS: 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.dovrebbe essere ignorato? Finora non ho ricevuto eccezioni ...
HB

15
@HB: in questo caso è sicuro ignorarlo. Oppure puoi semplicemente creare il flusso nella chiamata del costruttore a StreamReader. L'avvertimento mi sembra falso, dato che i documenti per IDisposable.Disposeesplicitamente dichiarano: "Se il metodo Dispose di un oggetto viene chiamato più di una volta, l'oggetto deve ignorare tutte le chiamate dopo il primo. L'oggetto non deve generare un'eccezione se il suo metodo Dispose è chiamato più volte. "
Jon Skeet,

5
@JonSkeet: In realtà c'è una pagina per questo , avevi ragione, questo è falso :)
HB

45

Questo è vecchio, ma oggi volevo fare qualcosa di simile e ho scoperto che le cose sono cambiate. Da .net 4.5, c'è un leaveOpenargomento:

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

L'unico problema è che non è del tutto ovvio cosa impostare per gli altri parametri. Ecco qualche aiuto:

Dalla pagina msdn per StreamReader Constructor (Stream):

Questo costruttore inizializza la codifica su UTF8Encoding, la proprietà BaseStream utilizzando il parametro stream e la dimensione del buffer interno su 1024 byte.

Ciò lascia solo ciò detectEncodingFromByteOrderMarksche a giudicare dal codice sorgente ètrue

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

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

Sarebbe bello se alcune di quelle impostazioni predefinite fossero esposte o se gli argomenti fossero facoltativi in ​​modo da poter specificare solo quelli che vogliamo.


Informazioni molto belle! Non ho mai sentito parlare di questo nuovo parametro e in realtà ha molto senso.
julealgon,

3
Per le persone pigre come me, la risposta breve per lasciare il flusso aperto sarebbe come:using (var streamReader = new StreamReader(myStream, Encoding.UTF8, true, 1024, true))
beawolf

29

Sì, lo fa. Puoi verificarlo osservando l'implementazione 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);
        }
    }
}

13

Sei anni di ritardo, ma forse questo potrebbe aiutare qualcuno.

StreamReader chiude la connessione quando viene eliminata. Tuttavia, "l'utilizzo di (Stream stream = ...) {...}" con StreamReader / StreamWriter può comportare l'eliminazione dello Stream due volte: (1) quando l'oggetto StreamReader è eliminato (2) e quando lo Stream utilizza blocco chiude. Ciò provoca un avviso CA2202 quando si esegue l'analisi del codice di VS.

Un'altra soluzione, presa direttamente dalla pagina CA2202 , è utilizzare un blocco try / finally. Impostato correttamente, questo chiuderà la connessione solo una volta.

Nella parte inferiore di CA2202 , Microsoft consiglia di utilizzare quanto segue:

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

invece di...

// Generates a CA2202 warning
using (Stream stream = new FileStream("file.txt", FileMode.Open))
using (XmlReader reader = new XmlReader (stream))
{
    // Use the reader object...
}

2
L'avvertimento è discusso anche nei commenti della risposta accettata. Jon Skeet offre alcuni consigli lì.
Marcin,

Inoltre, tenere presente che l'istruzione using viene effettivamente convertita dal compilatore in un blocco try-finally.
Jason Kelley,

2

Sì. Chiamare Dispose () e IDisposable (cosa che "usa") dovrebbe fare in modo che un oggetto ripulisca tutte le sue risorse. Ciò include lo svuotamento dei flussi e la chiusura dei descrittori di file.

Se, nel tuo caso, vuoi passarlo ad altri metodi, allora devi assicurarti che quei metodi non eseguano la loro lettura / scrittura in un blocco usando.



-2

lo stream eliminato "usando" la parola chiave o chiamando disposly esplicitamente

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.