.NET 中使用了一种相当常见的模式来测试类的功能。这里我将使用 Stream 类作为示例,但该问题适用于使用此模式的所有类。
该模式是提供一个名为 CanXXX 的布尔属性来指示该类上的能力 XXX 可用。例如,Stream 类具有 CanRead、CanWrite 和 CanSeek 属性,以指示可以调用 Read、Write 和 Seek 方法。如果属性值为 false,则调用相应的方法将导致抛出 NotSupportedException。
来自流类的 MSDN 文档:
根据底层数据源或存储库,流可能仅支持其中一些功能。应用程序可以使用 CanRead、CanWrite 和 CanSeek 属性查询流的功能。
CanRead 属性的文档:
在派生类中重写时,获取指示当前流是否支持读取的值。
如果从 Stream 派生的类不支持读取,则对 Read、ReadByte 和 BeginRead 方法的调用将引发 NotSupportedException。
我看到很多代码都是这样写的:
if (stream.CanRead)
{
stream.Read(…)
}
请注意,没有同步代码以任何方式锁定流对象——其他线程可能正在访问它或它引用的对象。也没有代码可以捕获 NotSupportedException。
MSDN 文档没有说明属性值不能随时间改变。事实上,当流关闭时,CanSeek 属性变为 false,展示了这些属性的动态特性。因此,没有合同保证上述代码片段中对 Read() 的调用不会引发 NotSupportedException。
我希望有很多代码会遇到这个潜在的问题。我想知道那些发现这个问题的人是如何解决这个问题的。什么设计模式适合这里?
我也很欣赏有关此模式有效性的评论(CanXXX、XXX() 对)。对我来说,至少在 Stream 类的情况下,这代表了一个试图做太多事情的类/接口,应该分成更基本的部分。缺乏严格的文档化合同使得测试变得不可能,实施变得更加困难!