I've spent quite a bit of time getting familiar with the .NET Stream classes. Usually I learn a lot by studying the class design of professional, commercial-grade frameworks, but I have to say that something doesn't quite smell right here.
System.IO.Stream is an abstract class representing a sequence of bytes. It has 10 abstract method/properties: Read, Write, Flush, Length, SetLength, Seek, Position, CanRead, CanWrite, CanSeek
. So many abstract members makes it cumbersome to derive from, because you have to override all those methods, even if most end up just throwing NotImplemented
.
Users of Stream classes are expected to call CanRead
, CanWrite
, or CanSeek
to find out the capabilities of the Stream, or I suppose just go ahead and call Read
, Write
, or Seek
and see if it throws NotImplemented
. Is it just me, or is this crummy design?
Though there are many nits I'd like to pick with the Stream
class design, the main one I'd like to ask about is this: Why didn't they use interfaces, like IReadable
, IWriteable
, ISeekable
, instead? Then a new Stream class could gracefully derive from the interfaces it supports. Isn't this the object-oriented way of doing things? Or am I missing something?
Update: It was pointed out that the value CanRead
et al can change at runtime—for example if a FileStream
is closed—and the point is taken. However, I remain unconvinced that this is a good design. From where I'm from, trying to read from a file that's already been closed is a bug, or at least an exceptional condition. (And thus throwing an exception is a natural way to handle this situation.)
Does this mean that every time I'm about to Read
from a Stream
, I should check CanRead
? And would that mean I should put a lock in place to avoid a race condition, if it's possible for the value to change sometime in between the CanRead
call and the Read
call?
Update Aug 7 2010: The consensus here seems to be the Stream design is pretty good as it stands. But let me ask one more time just to be 100% sure: People are writing something like this every time they read from a Stream?
// s is a Stream
lock(s)
{
if (s.CanRead)
{
s.Read(buf, 0, buf.Length);
}
}