Schließt das Entsorgen von Streamreader den Stream?

166

Ich sende einen Stream an Methoden zum Schreiben und verwende bei diesen Methoden einen binären Reader / Wrtier. Wenn der Leser / Schreiber entweder von usingoder nur wenn nicht darauf verwiesen wird, wird der Stream ebenfalls geschlossen?

Ich würde einen BinaryReader / Writer senden, aber ich verwende auch einen StreamReader (vielleicht sollte ich das umgehen. Ich verwende das nur für GetLine und ReadLine). Dies ist ziemlich problematisch, wenn der Stream jedes Mal geschlossen wird, wenn ein Schreiber / Leser geschlossen wird.

Nefzen
quelle

Antworten:

204

Ja, StreamReader, StreamWriter, BinaryReaderund BinaryWriteralle in der Nähe / entsorgen ihre zugrunde liegenden Ströme , wenn Sie anrufen Disposeauf sie. Sie entsorgen den Stream jedoch nicht, wenn der Leser / Schreiber nur Müll ist - Sie sollten den Leser / Schreiber immer entsorgen, vorzugsweise mit einer usingErklärung. (Tatsächlich hat keine dieser Klassen Finalisierer und sollte es auch nicht haben.)

Persönlich bevorzuge ich auch eine using-Anweisung für den Stream. Sie können usingAnweisungen ohne geschweifte Klammern ganz ordentlich verschachteln :

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

Obwohl die usingAnweisung für den Stream etwas redundant ist (es sei denn, der StreamReaderKonstruktor löst eine Ausnahme aus), halte ich es für eine bewährte Methode, wenn Sie StreamReaderden Stream loswerden und ihn zu einem späteren Zeitpunkt direkt verwenden, haben Sie bereits die richtige Verfügung Semantik.

Jon Skeet
quelle
2
Oh gut, es passiert nur beim Aufruf von Dispose, nicht beim angeblichen Finalisieren.
Nefzen
1
@Nefzen: Das liegt daran, dass es keine Garantie gibt, in welcher Reihenfolge Ihre Objekte fertiggestellt werden. Wenn sowohl der StreamReader als auch der zugrunde liegende Stream zur Finalisierung berechtigt sind, finalisiert der GC möglicherweise zuerst den Stream - dann hätte der Streamreader keinen Verweis auf den Stream. Aus diesem Grund können Sie nicht verwaltete Ressourcen nur innerhalb einer Finalisierung freigeben (z. B. schließt ein FileStream sein Windows-Dateihandle in seiner Finalisierung). Oh, und natürlich, wenn Sie nie entsorgen, wird der Stream schließlich noch gesammelt (und die Datei geschlossen). Es ist nur eine sehr schlechte Praxis, einen Stream nicht zu entsorgen.
JMarsch
13
Diese Verschachtelung führt dazu, dass sich der VS-Code-Analysator beschwert: 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.Sollte das einfach ignoriert werden? Ich habe bisher keine Ausnahmen bekommen ...
HB
15
@HB: In diesem Fall ist es sicher, es zu ignorieren. Oder Sie können den Stream einfach im Konstruktoraufruf an erstellen StreamReader. Die Warnung erscheint mir falsch, da in den Dokumenten IDisposable.Disposeexplizit angegeben wird: "Wenn die Dispose-Methode eines Objekts mehrmals aufgerufen wird, muss das Objekt alle Aufrufe nach dem ersten ignorieren. Das Objekt darf keine Ausnahme auslösen, wenn die Dispose-Methode lautet." mehrfach angerufen. "
Jon Skeet
5
@ JonSkeet: Eigentlich gibt es eine Seite dafür , du hattest Recht , das ist Schwindel :)
HB
45

Dies ist eine alte, aber ich wollte heute etwas Ähnliches tun und stellte fest, dass sich die Dinge geändert haben. Seit .net 4.5 gibt es ein leaveOpenArgument:

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

Das einzige Problem ist, dass es nicht ganz offensichtlich ist, was für die anderen Parameter eingestellt werden soll. Hier ist eine Hilfe:

Von der msdn-Seite für StreamReader Constructor (Stream):

Dieser Konstruktor initialisiert die Codierung mit UTF8Encoding, die BaseStream-Eigenschaft mithilfe des Stream-Parameters und die interne Puffergröße mit 1024 Byte.

Damit bleibt nur, detectEncodingFromByteOrderMarkswas nach dem Quellcode zu urteilen isttrue

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

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

Es wäre schön, wenn einige dieser Standardeinstellungen verfügbar wären oder wenn die Argumente optional wären, damit wir nur die gewünschten angeben könnten.

Acarlon
quelle
Sehr nette Infos! Ich habe noch nie von diesem neuen Parameter gehört und er macht tatsächlich sehr viel Sinn.
Julealgon
3
Für faule Leute wie mich wäre die kurze Antwort, um den Strom offen zu lassen, wie using (var streamReader = new StreamReader(myStream, Encoding.UTF8, true, 1024, true))
folgt
29

Ja tut es. Sie können dies überprüfen, indem Sie sich die Implementierung mit Reflector ansehen.

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
quelle
13

Sechs Jahre zu spät, aber vielleicht hilft das jemandem.

StreamReader schließt die Verbindung, wenn sie entsorgt wird. "Using (Stream stream = ...) {...}" mit StreamReader / StreamWriter kann jedoch dazu führen, dass der Stream zweimal entsorgt wird: (1) wenn das StreamReader-Objekt entsorgt wird (2) und wenn der Stream using block ist schließt. Dies führt zu einer CA2202-Warnung, wenn die Code-Analyse von VS ausgeführt wird.

Eine andere Lösung, die direkt von der CA2202- Seite übernommen wurde, ist die Verwendung eines try / finally-Blocks. Bei korrekter Einrichtung wird die Verbindung nur einmal geschlossen.

Microsoft empfiehlt, im unteren Bereich von CA2202 Folgendes zu verwenden:

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

anstatt...

// Generates a CA2202 warning
using (Stream stream = new FileStream("file.txt", FileMode.Open))
using (XmlReader reader = new XmlReader (stream))
{
    // Use the reader object...
}
Sunsetquest
quelle
2
Die Warnung wird auch in den Kommentaren der akzeptierten Antwort besprochen. Jon Skeet gibt dort einige Ratschläge.
Marcin
Beachten Sie außerdem, dass die using-Anweisung vom Compiler tatsächlich in einen try-finally-Block konvertiert wird.
Jason Kelley
2

Ja. Durch Aufrufen von Dispose () on und IDisposable (was "using" bewirkt) sollte ein Objekt alle seine Ressourcen bereinigen. Dies umfasst das Leeren und Schließen der Dateideskriptoren durch Streams.

Wenn Sie es in Ihrem Fall an andere Methoden übergeben möchten, müssen Sie sicherstellen, dass diese Methoden nicht in einem using-Block lesen / schreiben.

Joe M.
quelle
-2

Der Stream wird entweder durch Verwenden des Schlüsselworts "using" oder durch expliziten Aufruf von dispose entsorgt

Ahmed Said
quelle