C# .NET流类的设计是否糟糕?

C# .NET流类的设计是否糟糕?,c#,.net,oop,stream,abstract-class,C#,.net,Oop,Stream,Abstract Class,我花了很多时间熟悉.NET流类。通常,我通过研究专业的、商业级框架的类设计学到了很多东西,但我不得不说,这里有些东西不太好闻 System.IO.Stream是表示字节序列的抽象类。它有10个抽象方法/属性:Read、Write、Flush、Length、SetLength、Seek、Position、CanRead、CanWrite、CanSeek。如此多的抽象成员使得从中派生变得很麻烦,因为您必须重写所有这些方法,即使大多数方法最终只是抛出NotImplemented 流类的用户需要调用Ca

我花了很多时间熟悉.NET流类。通常,我通过研究专业的、商业级框架的类设计学到了很多东西,但我不得不说,这里有些东西不太好闻

System.IO.Stream是表示字节序列的抽象类。它有10个抽象方法/属性:
Read、Write、Flush、Length、SetLength、Seek、Position、CanRead、CanWrite、CanSeek
。如此多的抽象成员使得从中派生变得很麻烦,因为您必须重写所有这些方法,即使大多数方法最终只是抛出
NotImplemented

流类的用户需要调用
CanRead
CanWrite
CanSeek
来了解流的功能,或者我想直接调用
Read
Write
Seek
,看看它是否抛出
未实现的
。只是我,还是这个蹩脚的设计

虽然我想在
类设计中挑很多毛病,但我想问的主要问题是:为什么他们不使用接口,比如
IReadable
IWriteable
ISeekable
?然后,一个新的流类可以优雅地从它支持的接口派生。这不是面向对象的方式吗?还是我遗漏了什么

更新:有人指出,
CanRead
等值可能在运行时发生变化,例如,如果关闭了
FileStream
,则取该点。然而,我仍然不相信这是一个好的设计。从我的家乡来看,试图读取已经关闭的文件是一个错误,或者至少是一个例外情况。(因此,抛出异常是处理这种情况的自然方式。)


这是否意味着每次我要从
流中
读取
,我都应该选中
CanRead
?如果值可能在
CanRead
调用和
Read
调用之间的某个时间发生变化,这是否意味着我应该设置一个锁以避免竞争条件

2010年8月7日更新:这里的共识似乎是,目前的河流设计相当不错。但让我再问一次,只是为了100%确定:人们每次阅读流时都在写这样的东西

//  s is a Stream

lock(s)
{
    if (s.CanRead)
    {
        s.Read(buf, 0, buf.Length);
    }
}

使用接口意味着“CanRead”的值在运行时无法更改。“FileStream”类根据文件的当前状态更改“CanRead”属性。

我认为这些类设计得很好。我更愿意检查一个属性,然后尝试做一些事情,并且必须捕获一个异常。在流类型为多个“类型”的情况下,接口不足。获取可读写流的方法将返回什么类型?我同意这个设计不是一个真正的面向对象的设计,但是你真的想用那种方式来处理流吗?如果流关闭或其他更改,某些属性可能会更改,在这种情况下会发生什么


我认为这个问题带来了一个非常有趣的实验,为什么不尝试设计自己的流相关类呢。将您的重新设计发布在CodePlex或Google Code上,这将是一次很棒的学习体验,并将为其他人提供一个潜在的有用库

他们可能没有使用接口,因为当时没有扩展方法。如果希望每个流都有默认的ReadByte方法,则需要使用类


几个月前,我写了一篇关于为什么我不喜欢IO.Stream以及我认为应该做什么的文章。本质上,它归结为流不是非常类型安全的。

接口可能被过度使用,这就是其中之一。我觉得现在的设计很棒。流可以在运行时更改功能这一事实意味着IReadable/IWritable/ISeekable不会消除对CanRead、CanWrite和CanSeek的需求,因此您只会增加复杂性,除了在派生类中消除少量存根方法和属性之外,没有任何实际好处

就我个人而言,我更喜欢流类更易于使用,而不是更易于编写,因为您只需编写一次并多次使用它。

您可以采用(Java*)方法编写一个大部分为空的
MyStream
类,该类继承了基本
类,但提供了大多数成员方法(例如,
CanSeek()
)并用合理的默认行为构建它们(例如,抛出
未实现的
)。然后,您真正的类只需扩展您的
MyStream
类,实现您真正需要的其余两个或三个方法

当您重新使用
MyStream
类时,您将节省大量对轮子的重新发明


*这在Java库中称为抽象适配器类。

类流使用可选的功能模式,您可以阅读更多。

我对流类的问题是长度属性-它没有指定如何实现未知、未指定或无限长的流

回答这个问题:可能吧

我非常同意@Strilanc的回答,是的,它的实现很差,但我想我还是继续发表我的想法

虽然使用可组合的接口和扩展方法(现在已经有了)来实现这些东西肯定会更干净,.NET1没有这些特性,所以我可以理解他们为什么选择这样设计

然而,现在我们有了真正有用的构造,比如泛型和扩展方法,我认为是时候重新访问许多原始类并用它们来标记它们了。当然,您首先需要一个替换API

将允许这些类在simul