I agree with your question. The biggest issue with this intentional side-effect is when developers don't know about it and are blindly following the "best practice" of surrounding a StreamReader with a using
. But it can cause some really hard to track down bugs when it is on a long-lived object's property, the best (worst?) example I've seen is
using (var sr = new StreamReader(HttpContext.Current.Request.InputStream))
{
body = sr.ReadToEnd();
}
The developer had no idea the InputStream is now hosed for any future place that expects it to be there.
Obviously, once you know the internals you know to avoid the using
and just read and reset the position. But I thought a core principle of API design was to avoid side effects, especially not destroying data you are acting upon. Nothing inherent about a class that supposedly is a "reader" should clear the data it reads when done "using" it. Disposing of the reader should release any references to the Stream, not clear the stream itself. The only thing I can think is that the choice had to be made since the reader is altering other internal state of the Stream, like the position of the seek pointer, that they assumed if you are wrapping a using around it you are intentionally going to be done with everything. On the other hand, just like in your example, if you are creating a Stream, the stream itself will be in a using
, but if you are reading a Stream that was created outside of your immediate method, it is presumptuous of the code to clear the data.
What I do and tell our developers to do on Stream instances that the reading code doesn't explicitly create is...
// save position before reading
long position = theStream.Position;
theStream.Seek(0, SeekOrigin.Begin);
// DO NOT put this StreamReader in a using, StreamReader.Dispose() clears the stream
StreamReader sr = new StreamReader(theStream);
string content = sr.ReadToEnd();
theStream.Seek(position, SeekOrigin.Begin);
(sorry I added this as an answer, wouldn't fit in a comment, I would love more discussion about this design decision of the framework)