I need to read a stream two times, from start to end.
But the following code throws an ObjectDisposedException: Cannot access a closed file
exception.
string fileToReadPath = @"<path here>";
using (FileStream fs = new FileStream(fileToReadPath, FileMode.Open))
{
using (StreamReader reader = new StreamReader(fs))
{
string text = reader.ReadToEnd();
Console.WriteLine(text);
}
fs.Seek(0, SeekOrigin.Begin); // ObjectDisposedException thrown.
using (StreamReader reader = new StreamReader(fs))
{
string text = reader.ReadToEnd();
Console.WriteLine(text);
}
}
Why is it happening? What is really disposed? And why manipulating StreamReader
affects the associated stream in this way? Isn't it logical to expect that a seekable stream can be read several times, including by several StreamReader
s?
This happens because the StreamReader
takes over 'ownership' of the stream. In other words, it makes itself responsible for closing the source stream. As soon as your program calls Dispose
or Close
(leaving the using
statement scope in your case) then it will dispose the source stream as well. Calling fs.Dispose()
in your case. So the file stream is dead after leaving the first using
block. It is consistent behavior, all stream classes in .NET that wrap another stream behave this way.
There is one constructor for StreamReader
that allows saying that it doesn't own the source stream. It is however not accessible from a .NET program, the constructor is internal.
In this particular case, you'd solve the problem by not using the using
-statement for the StreamReader
. That's however a fairly hairy implementation detail. There's surely a better solution available to you but the code is too synthetic to propose a real one.
The purpose of Dispose()
is to clean up resources when you're finished with the stream. The reason the reader impacts the stream is because the reader is just filtering the stream, and so disposing the reader has no meaning except in the context of it chaining the call to the source stream as well.
To fix your code, just use one reader the entire time:
using (FileStream fs = new FileStream(fileToReadPath, FileMode.Open))
using (StreamReader reader = new StreamReader(fs))
{
string text = reader.ReadToEnd();
Console.WriteLine(text);
fs.Seek(0, SeekOrigin.Begin); // ObjectDisposedException not thrown now
text = reader.ReadToEnd();
Console.WriteLine(text);
}
Edited to address comments below:
In most situations, you do not need to access the underlying stream as you do in your code (fs.Seek
). In these cases, the fact that StreamReader
chains its call to the underlying stream allows you to economize on the code by not using a usings
statement for the stream at all. For example, the code would look like:
using (StreamReader reader = new StreamReader(new FileStream(fileToReadPath, FileMode.Open)))
{
...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With