2009-06-30 3 views
142

io mando un flusso di metodi per scrivere, e in quei metodi che sto usando un binario lettore/wrtier. Quando il lettore/scrittore viene disposta, mediante using o solo quando non viene fatto riferimento, è il flusso chiuso pure ??Lo streamreader di smaltimento chiude lo stream?

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

risposta

166

Sì, StreamReader, StreamWriter, BinaryReader e BinaryWriter tutti vicino/smaltire i loro flussi sottostanti quando si chiama Dispose su di loro. Essi Non smaltire il flusso se il lettore/scrittore è solo spazzatura raccolte anche se - si dovrebbe sempre disporre del lettore/scrittore, preferibilmente con una dichiarazione using. (In realtà, nessuna di queste classi hanno finalizzatori, né dovrebbe hanno.)

Personalmente preferisco avere un'istruzione using per il flusso pure. È possibile nidificare using dichiarazioni senza bretelle abbastanza ordinatamente:

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

Anche se la dichiarazione using per il flusso è un po 'ridondante (a meno che il costruttore StreamReader genera un'eccezione), ritengo che la cosa migliore pratica come se poi ci si libera del StreamReader e basta utilizzare lo streaming direttamente in un secondo momento, avrai già la giusta semantica di smaltimento.

+2

Oh bene, succede solo quando si chiama Dispose, non quando presumibilmente la finalizzazione. – Nefzen

+1

@Nefzen: Questo perché non è garantito quale ordine i tuoi oggetti saranno finalizzati. Se sia StreamReader che il flusso sottostante sono idonei per la finalizzazione, il GC potrebbe prima finalizzare il flusso, quindi il lettore di stream non avrebbe un riferimento allo streaming. Per questo motivo, è possibile rilasciare solo risorse non gestite all'interno di un finalize (ad esempio, FileStream chiude l'handle del file di Windows nella sua finalizzazione). Oh, e, naturalmente, se non lo smaltisci mai, il flusso verrà comunque raccolto (e il file verrà chiuso). È solo una pessima pratica non distribuire un flusso. – JMarsch

+9

Questo annidamento fa sì che l'analizzatore del codice VS si lamenti: 'CA2202: Microsoft.Usage: Object 'stream' può essere eliminato più volte nel metodo '...'. Per evitare di generare una eccezione System.ObjectDisposedException, non dovresti chiamare Dispose più di una volta su un oggetto. Deve essere semplicemente ignorato? Non ho avuto eccezioni fino ad ora ... –

-3

torrente disposte mediante "con" parola chiave o chiamando Dispose esplicitamente

27

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

Sì. Chiamando Dispose() su e IDisposable (che "usando" lo fa) dovrebbe rendere un oggetto ripulire tutte le sue risorse. Ciò include flussi che scaricano e chiudono i descrittori di file.

Se, nel tuo caso, si desidera passare in ad altri metodi, allora avete bisogno di fare in modo che questi metodi non fanno la loro lettura/scrittura in un blocco utilizzando.

31

Questo è un vecchio, ma volevo fare qualcosa di simile oggi e ha scoperto che le cose sono cambiate. Poiché .net 4.5, v'è un argomento leaveOpen:

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

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

Da the msdn page per StreamReader Costruttore (Stream):

Questo costruttore inizializza la codifica UTF8Encoding, la proprietà BaseStream utilizzando il parametro flusso, e la dimensione interna buffer 1024 byte.

Questo lascia solo detectEncodingFromByteOrderMarks che a giudicare da the source code è true

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

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

Sarebbe bello se alcuni di questi valori predefiniti sono stati esposti o se le argomentazioni erano opzionali in modo da poter specificare solo quelli quello che vogliamo

+0

Informazioni molto belle! Mai sentito parlare di questo nuovo parametro e in realtà ha molto senso. – julealgon

+0

Non avevo notato la nuova aggiunta in 4.5, grazie! – TimothyP

8

Sei anni di ritardo ma forse questo potrebbe aiutare qualcuno.

StreamReader chiude la connessione quando viene eliminata. Tuttavia, "usando (Stream stream = ...) {...}" con StreamReader/StreamWriter può comportare che il flusso venga eliminato due volte: (1) quando l'oggetto StreamReader è disposto (2) e quando il flusso utilizza il blocco chiude. Ciò si traduce in un avviso CA2202 durante l'esecuzione dell'analisi del codice di VS.

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

Nella parte inferiore della CA2202, Microsoft consiglia di utilizzare il seguente:

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

Sì, questo è stato molto utile per me, ora 7 anni dopo :) – Sebastian

+0

L'avviso è discusso anche nei commenti della risposta accettata. Jon Skeet offre alcuni consigli lì. – Marcin