+8  A: 

Using interfaces would mean that the value of "CanRead" couldn't be changed at runtime. The "FileStream" class changes the "CanRead" property based on the current state of the file.

David
Could you elaborate on that? I don't get why CanRead couldn't be changed at runtime.
devoured elysium
If you close the stream CanRead = false
Bob
devoured elysium: the OP is suggesting that readable streams would implement IReadable rather than having a CanRead property. With CanRead, if a stream becomes unreadable, it can return false from CanRead. But with IReadable, it wouldn't be able to remove IReadable from its type declaration.
itowlson
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?
I. J. Kennedy
The CanRead isn't the only reason you would need a lock around a stream. A Stream is a single resource that will cause plenty of problems if you pound on it from several Threads in an unorganized manner.
Matthew Whited
+9  A: 

I think the classes are designed nicely. I would much rather check a property then attempt to do something and have to catch an exception. Interfaces fall short in the case of stream types that are of multiple "types". What type would be returned from a method that gets you a readable and writable stream? I agree the design isn't a true Object Oriented Design, but do you really want to treat streams in that manner? Some of the properties can change if the stream is closed or something else changes, what would happen in that case?

I think this question brings up a really interesting experiment though, why not try to design your own stream related classes. Publish your redesign on CodePlex or Google Code, it would be a great learning experience and would result in a potentially useful library for others to use.

Bob
> Interfaces fall short in the case of stream types that are of multiple "types".I would say the opposite. You can derive from only one class but as many interfaces as you like.
I. J. Kennedy
Yet if you really need/want multiple classes you can build adapter classes to wrap several types and have implict casting operators.
Matthew Whited
@I. J. Kennedy, My point is if you get the stream back from a method call, what type will it be? How will you know it is readable or writable? Type checking?
Bob
The capabilities would be part of the type, where they should be. The method would not return a Stream, but an IReadableStream.
Strilanc
So if the stream was readable and writeable you'd get an IReadableWritableStream?
Bob
No, you could get an object that implements both IReadable and IWritable.
siride
+2  A: 

They probably didn't use interfaces because, at the time, there were no extension methods. If you wanted every stream to have things like a default ReadByte method, you needed to use a class.

I wrote a blog post a couple months back about why I don't like IO.Stream and what I thought should be done. Essentially it boils down to streams not being very type-safe.

Strilanc
+1 I read your blog post, and yes, that is exactly what I'm talking about.
I. J. Kennedy
+5  A: 

Interfaces can be overused, and this would be one of those cases. I think the current design is great. The fact that streams can change capabilities at runtime means that IReadable/IWritable/ISeekable would not obviate the need for CanRead, CanWrite and CanSeek, so you would just be increasing the complexity for no real gain other than eliminating a handful of stub methods and properties in your derived classes.

Personally I prefer that a stream class be easier to use than easier to write, because you'll be writing it once and using it many times.

Gerald
@Gerald: Good point, but it's more than just the annoying stubs. Take a look at the updated part of the question and tell me if I'm off base. Seems like even *using* this Stream design correctly is difficult too.
I. J. Kennedy
@IJ, to touch on your update, it could be either an exceptional condition, or an expected condition. You may have a design where a stream is temporarily unreadable, and since you know that will be the case it's not really exceptional. In that case you'll want to use a lock. On the other hand, you can also rely on exceptions if you want to, where it is would truly be an exceptional case.
Gerald
A: 

You could take the (Java*) approach of writing a mostly-empty MyStreamclass that inherits the base Streamclass, but provides most of the member methods (e.g., CanSeek()) and embuing them with reasonable default behavior (e.g., throwing NotImplemented). Then your real class simply extends your MyStreamclass, implementing the two or three remaining methods that you really need.

As you reuse your MyStreamclass, you'll save a lot of reinventing of the wheel.

* This is called an abstract adapter class in the Java libraries.

Loadmaster
+2  A: 

Class Stream uses Optional feature pattern, you can read more here.

QrystaL
A: 

My issue with the stream class is the Length property - it doesn't specify how to implement a stream of unknown, unspecified, or infinite length!

Arafangion
See http://msdn.microsoft.com/en-us/library/system.io.stream.length.aspx If the stream doesn't support seeking (which would be the case for something with unknown length) then throw a `NotSupportedException`.
Richard