182

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.

Chris Cudmore
  • 29,793
  • 12
  • 57
  • 94
Nefzen
  • 7,819
  • 14
  • 36
  • 34

7 Answers7

222

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.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 2
    oh good, it only happens when calling Dispose, not when supposedly finalizing. – Nefzen Jun 30 '09 at 18:29
  • 1
    @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. – JMarsch Jun 30 '09 at 19:20
  • 14
    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... – H.B. Dec 12 '11 at 09:55
  • 16
    @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." – Jon Skeet Dec 12 '11 at 10:01
  • @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. – H.B. Dec 12 '11 at 10:13
  • 1
    @H.B.: Possibly. If that's the case, the wording should definitely be improved. – Jon Skeet Dec 12 '11 at 10:14
  • 6
    @JonSkeet: Actually there's a [page for this](http://msdn.microsoft.com/en-us/library/ms182334.aspx), you were correct, this is bogus :) – H.B. Dec 12 '11 at 10:17
  • I was about to use your example until I read this latest post about "CA2202: Do not dispose objects multiple times" http://stackoverflow.com/q/12000136/427684 Should we use the more verbose code as [Microsoft recomends](http://msdn.microsoft.com/en-us/library/ms182334.aspx) or following the adivce of @JHubbard80 in that same SO question http://stackoverflow.com/a/12011186/427684 – Paul C Nov 13 '13 at 16:27
  • 1
    Reading further it would seem @JHubbard80 's example gives a different warning CA2000: Dispose objects before losing scope – Paul C Nov 13 '13 at 16:30
  • 1
    @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 :) – Jon Skeet Nov 13 '13 at 16:30
  • Does the same apply to database connection, if dispose() is called does the connection close or we need to close it first? – Ashraf Sada Jan 22 '15 at 09:04
  • @Ashraf_Sada: `Dispose` closes the connection. – Jon Skeet Jan 22 '15 at 09:15
  • Thanks, for your confirmation, I think to say dispose closes any object is true! – Ashraf Sada Jan 22 '15 at 09:24
  • @Ashraf_Sada: Well it's up to the API, of course - but that's the convention, for "closable" types. – Jon Skeet Jan 22 '15 at 09:44
  • @Edge: No, it's a C# language feature. But I believe C++ has *similar* mechanisms due to deterministic destructors - look up RAII, basically. – Jon Skeet Apr 28 '15 at 12:35
  • 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(); } – Bisneto Mar 05 '20 at 15:03
  • @Bisneto: Please ask a new question with a [mcve] - `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...) – Jon Skeet Mar 05 '20 at 16:08
  • 1
    See https://github.com/microsoft/referencesource/blob/master/mscorlib/system/io/streamwriter.cs#L231 for example - I haven't found the .NET Core implementation yet, but I expect it to be the same. – Jon Skeet Mar 05 '20 at 16:18
55

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.

acarlon
  • 16,764
  • 7
  • 75
  • 94
  • 1
    Very nice info! Never heard of this new parameter and it actually makes a lot of sense. – julealgon Jun 02 '14 at 17:00
  • 7
    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))` – MÇT Oct 31 '18 at 06:56
  • 5
    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))` – Tara McGrew Dec 11 '20 at 01:57
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);
        }
    }
}
Brian Rasmussen
  • 114,645
  • 34
  • 221
  • 317
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...
}
SunsetQuest
  • 8,041
  • 2
  • 47
  • 42
  • 2
    The warning is discussed in the comments of the accepted answer as well. Jon Skeet offers some advice there. – Marcin Aug 23 '17 at 18:51
  • Also, be aware that the using statement is actually converted into a try-finally block by the compiler. – Jason Kelley Aug 27 '18 at 22:51
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.

Joe M
  • 251
  • 1
  • 6
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?

Community
  • 1
  • 1
Aaron Murgatroyd
  • 1,506
  • 19
  • 22
-2

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

Ahmed
  • 7,148
  • 12
  • 57
  • 96