Does disposing streamreader close the stream?
Asked Answered
S

7

186

I am sending a stream to methods to write on, and in those methods I am using a binary reader/wrtier. When the reader/writer gets disposed, either by using or just when it is not referenced, is the stream closed as well??

I would send a BinaryReader/Writer, but I am using a StreamReader too (maybe I should go around that. I am only using that for GetLine and ReadLine). This is quite troublesome if it closes the stream each time a writer/reader gets closed.

Stereochemistry answered 30/6, 2009 at 18:17 Comment(0)
L
228

Yes, StreamReader, StreamWriter, BinaryReader and BinaryWriter all close/dispose their underlying streams when you call Dispose on them. They don't dispose of the stream if the reader/writer is just garbage collected though - you should always dispose of the reader/writer, preferrably with a using statement. (In fact, none of these classes have finalizers, nor should they have.)

Personally I prefer to have a using statement for the stream as well. You can nest using statements without braces quite neatly:

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

Even though the using statement for the stream is somewhat redundant (unless the StreamReader constructor throws an exception) I consider it best practice as then if you get rid of the StreamReader and just use the stream directly at a later date, you'll already have the right disposal semantics.

Leonhard answered 30/6, 2009 at 18:23 Comment(18)
oh good, it only happens when calling Dispose, not when supposedly finalizing.Stereochemistry
@Nefzen: That's because there is no guarantee what order your objects will be finalized. If both the StreamReader and the underlying Stream are eligible for finalization, the GC might finalize the stream first -- then streamreader would not have a reference to stream. For this reason, you can only release unmanaged resources inside of a finalize (for example, a FileStream closes its windows file handle in its finalize). Oh, and of course, if you never dispose, the stream will still be collected eventually(and the file closed). It's just a very bad practice to not dispose a stream.Chauffeur
This nesting causes the VS code analyzer to complain: 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. Should that just be ignored? I did not get any exceptions so far...Magellan
@H.B.: It's safe to ignore it in this case. Or you can just create the stream in the constructor call to StreamReader. The warning looks bogus to me, given that the docs for IDisposable.Dispose explicitly state: "If an object's Dispose method is called more than once, the object must ignore all calls after the first one. The object must not throw an exception if its Dispose method is called multiple times."Leonhard
@JonSkeet: Maybe the concern here was that if an object is disposed in multiple places it is harder to tell at which point the object can be accessed and hence said exception may occur if one does end up accessing the disposed stream.Magellan
@H.B.: Possibly. If that's the case, the wording should definitely be improved.Leonhard
@JonSkeet: Actually there's a page for this, you were correct, this is bogus :)Magellan
I was about to use your example until I read this latest post about "CA2202: Do not dispose objects multiple times" https://mcmap.net/q/56059/-using-statement-filestream-and-or-streamreader-visual-studio-2012-warnings/427684 Should we use the more verbose code as Microsoft recomends or following the adivce of @JHubbard80 in that same SO question https://mcmap.net/q/56059/-using-statement-filestream-and-or-streamreader-visual-studio-2012-warningsHello
Reading further it would seem @JHubbard80 's example gives a different warning CA2000: Dispose objects before losing scopeHello
@CodeBlend: Personally I wouldn't worry either way. In other situations I use using (StreamReader reader = new StreamReader(/* create stream here */, Encoding.Whatever). I think this is just code analysis being overly picky :)Leonhard
Does the same apply to database connection, if dispose() is called does the connection close or we need to close it first?Daytime
@Ashraf_Sada: Dispose closes the connection.Leonhard
Thanks, for your confirmation, I think to say dispose closes any object is true!Daytime
@Ashraf_Sada: Well it's up to the API, of course - but that's the convention, for "closable" types.Leonhard
@Edge: No, it's a C# language feature. But I believe C++ has similar mechanisms due to deterministic destructors - look up RAII, basically.Leonhard
If I don't call Close in this code, the result is "The file is being used by another process" using (StreamWriter sw = File.AppendText(_configuration["Log:File"])) { sw.WriteLine(text); sw.Close(); }Silencer
@Bisneto: Please ask a new question with a minimal reproducible example - StreamWriter.Dispose (called implicitly by your using statement) really should be closing the stream, so I'd be very surprised to see that. (Will double check that though...)Leonhard
See github.com/microsoft/referencesource/blob/master/mscorlib/… for example - I haven't found the .NET Core implementation yet, but I expect it to be the same.Leonhard
A
56

This is an old one, but I wanted to do something similar today and found that things have changed. Since .net 4.5, there is a leaveOpen argument:

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

The only problem is that it is not entirely obvious what to set for the other parameters. Here is some help:

From the msdn page for StreamReader Constructor (Stream):

This constructor initializes the encoding to UTF8Encoding, the BaseStream property using the stream parameter, and the internal buffer size to 1024 bytes.

That just leaves detectEncodingFromByteOrderMarks which judging by the source code is true

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

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

It would be nice if some of those defaults were exposed or if the arguments were optional so that we could just specify the ones that we want.

Aalii answered 12/1, 2014 at 4:55 Comment(3)
Very nice info! Never heard of this new parameter and it actually makes a lot of sense.Fadein
For lazy people like me, the short answer to leave stream open would be like: using (var streamReader = new StreamReader(myStream, Encoding.UTF8, true, 1024, true))Ordinance
You can use named arguments to specify the ones you want, and keep the default values for the rest: using (var streamReader = new StreamReader(myStream, leaveOpen: true))Antoinette
D
30

Yes, it does. You can verify this by looking at the implementation with 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);
        }
    }
}
Driving answered 30/6, 2009 at 18:22 Comment(0)
V
17

Six years late but maybe this might help someone.

StreamReader does close the connection when it is disposed. However, "using (Stream stream = ...){...}" with StreamReader/StreamWriter can result in the Stream being disposed of twice: (1) when the StreamReader object is disposed (2) and when the Stream using block closes. This results in a CA2202 warning when running VS's code analysis.

Another solution, taken directly from the CA2202 page, is to use a try/finally block. Setup correctly, this will only close the connection once.

Near the bottom of CA2202, Microsoft recommends to use the following:

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

instead of...

// Generates a CA2202 warning
using (Stream stream = new FileStream("file.txt", FileMode.Open))
using (XmlReader reader = new XmlReader (stream))
{
    // Use the reader object...
}
Vickievicksburg answered 13/9, 2015 at 21:11 Comment(2)
The warning is discussed in the comments of the accepted answer as well. Jon Skeet offers some advice there.Borneol
Also, be aware that the using statement is actually converted into a try-finally block by the compiler.Coastwise
C
2

Yes. Calling Dispose() on and IDisposable (which "using" does) should make an object clean up all of its resources. This includes streams flushing and closing their file descriptors.

If, in your case, you want to pass it in to other methods, then you need to make sure that those methods do not do their reading/writing in a using block.

Cultigen answered 30/6, 2009 at 18:22 Comment(0)
P
1

An easy way to fix this if you need to is to override the StreamWriter classes Dispose method. See my post here for the code on how to do it:

Does .Disposing a StreamWriter close the underlying stream?

Purveyor answered 21/7, 2011 at 23:53 Comment(0)
C
-2

the stream disposed either by "using" keyword or calling dispose explicitly

Creaturely answered 30/6, 2009 at 18:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.