Will closing a FileStream close the StreamReader?
Asked Answered
H

7

9

If I use a FileStream to create a StreamReader, will the StreamReader close when I close the FileStream or will I need to close the StreamReader too?

public void ReadFile()
{
    var file = new FileStream("c:\file.txt", FileMode.Open, FileAccess.Read);
    var reader = new StreamReader(file);

    try
    {
        txtFile.Text = reader.ReadToEnd();
    }
    catch (Exception)
    {
        throw;
    }
    finally
    {
        file.Close();
    }
}
Hospers answered 24/11, 2009 at 19:0 Comment(1)
possible duplicate of Does disposing streamreader close the stream?Mansard
M
8

Essentially yes. You don't actually have to close a StreamReader. If you do, all it does is closes the underlying stream.

@Bruno makes a good point about closing the outer-most wrapper. It is good practice to close the outer-most stream and let it close underlying streams in order to ensure all resources are released properly.

From Reflector...

public class StreamReader : TextReader
{
    public override void Close()
    {
        this.Dispose(true);
    }

    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);
            }
        }
    }
}
Mixie answered 24/11, 2009 at 19:2 Comment(5)
IMO, this is a really bad practice. In the future, BCL designers might opt to add some additional structures that need to be disposed...Tobi
@bruno conde: you yourself said that the top most wrapper should be disposed. StreamReader is the top wrapper for FileReader. Where is the problem?Binnings
@Kamarey, the problem is that the OP is only disposing of FileStream.Tobi
@bruno conde: Agree with you, the OP should close the StreamReader instead.Binnings
@Bruno, I agree with you that the outer-most stream should be closed. Even if it doesn't do anything interesting other than close the inner stream, it is a good idea for exactly the reason you state, forward compatibility. It is also good practice so that you don't forget to do it when working with other streams that do need to be closed.Mixie
T
6

No. You should close the reader instead. In practice, this might not present any problem but, the StreamReader could add some overhead that might need to be cleaned. So you should always close the top most wrapper.

Tobi answered 24/11, 2009 at 19:2 Comment(0)
H
6

You could also just use the File.ReadAllText method:

txtFile.Text = File.ReadAllText(@"c:\file.txt");
Holtz answered 24/11, 2009 at 19:7 Comment(2)
How does that work? Does it just open a connection, read the entire file, then close the file?Hospers
People use FileStream because they can't use ReadAllText. Only FileSream allows access to a shared file without crash. You can prove by reading a watched file that is opened by Notepad++ before then try to save this file with Notepad++.Radiothorium
M
2

You don't need to close the StreamReader because it doesn't own any unmanaged resources. Closing the FileStream is sufficient. You can rewrite your code with using like this:

public void ReadFile()
{
    using (var file = new FileStream("c:\file.txt", FileMode.Open, FileAccess.Read))
    {
        txtFile.Text = new StreamReader(file).ReadToEnd();
    }
}

In general if you are in doubt it is best to be safe and Dispose all IDisposable objects when you have finished using them.

public void ReadFile()
{
    using (FileStream file = new FileStream("c:\file.txt", FileMode.Open, FileAccess.Read))
    {
        using (StreamReader streamReader = new StreamReader(file))
        {
            txtFile.Text = streamReader.ReadToEnd();
        }
    }
}
Marguerite answered 24/11, 2009 at 19:4 Comment(2)
The "using" I know disposes of the object, does this ensure that the connection is close as well?Hospers
@norlando Yes it does although in general it is down to each individual class implementation about what happens on a dispose. In the case of standard streams, it follows what is the logical steps of flushing the buffers, closing the stream and disposing of any managed and unmanaged objects.Capers
W
0

No. The best thing to do is close them in reverse order that you opened them.

Womera answered 24/11, 2009 at 19:2 Comment(0)
P
0

It seems to me that the best way to do this overall would be to have the FileStream closing only itself. It does not implicitly have knowledge of anything that exists in a layer above itself, so it is effectively an error for it to do anything that would affect those higher layers.

Having said that, the higher-level constructs should not axiomatically assume anything about any supplied underlying layer as well, or if they do so, they should do so explicitly:

1) If it was created from an existing stream, then the higher-level construct should be able to be closed INDEPENDENTLY of the underlying stream (effectively just disposing of any resources it allocated for its own use), or closed INCLUDING the underlying stream. These should be two distinct function calls, for example Close() and CloseSelf() (if this were to be implemented in such a way as to be backward compatible with existing code).

2) If it was not created from an existing stream (that is, the constructor had to create the underlying stream), then closing the higher-level construct should force the underlying stream to close as well, since in that case the underlying stream is an implicit part of the higher-level construct. In this case, CloseSelf() would simply call Close().

It seems wasteful to implement these classes the way it was done. If you plan to use the same file for (as an example) serial input and serial output, you are effectively forced by the system to treat it as two distinct entities if you wish to gain access to the higher level functionality of the descendant classes. Your alternative is to stick to the lower level construct and implement the higher-level functionality yourself - effectively re-implementing your own special versions of descendant classes that already exist.

If it were done as described above, the typical functionality would be as simple to implement as it is now, but for more sophisticated applications one would retain the ability to place a single lock on a file and re-purpose it as required when required as opposed to having to relinquish the lock and all associated resources and then instantly reallocate them all over again - adding overhead and memory fragmentation to the system without any valid reason.

Under the existing conditions, though, the correct thing is clear. The FileStream cannot be assumed to know anything about any object it becomes a part of, so you must close the outermost enclosing construct. This applies regardless of whether or not it works either way, as Bruno et al have noted, and for the reason they gave - compatibility. Assumption is the great-granddaddy of the ugliest bugs.

Poise answered 11/7, 2011 at 21:2 Comment(0)
P
0

An interesting aside is that closing a StreamReader or writer will affect the read/write status of the owning FileStream. This seems to mean that you cannot use a StreamReader and then a StreamWriter using the same filestream.

Pucka answered 27/10, 2011 at 9:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.