ReadOnlyCollection类是Bad Design的一个很好的例子吗?

goe*_*ing 20 c# collections frameworks liskov-substitution-principle

查看ReadOnlyCollection类的规范,它确实实现了IList接口.

IList接口具有Add/Update/Read方法,我们称之为接口的前置条件.在我的应用程序的任何地方,如果我有一个IList我应该能够做所有这些操作.

但是,如果我在代码中的某处返回ReadOnlyCollection并尝试调用.Add(...)方法呢?它会抛出NotSupportedException.你认为这是一个糟糕设计的好例子吗?另外,这个类是否打破了Liskov替换原则

为什么Microsoft以这种方式实现?是否更容易(和更好)使这个ReadOnlyCollection只实现IEnumerable接口(顺便说一下,已经只读)?

Tim*_*mwi 10

是的,确实是糟糕的设计..NET中缺少集合接口:没有只读接口.

您是否知道string[]实现IList<string>(和其他类型的同上)?这有同样的问题:你会期望你可以调用AddRemove在界面上,但它会抛出.

不幸的是,在不破坏向后兼容性的情况下,这不能再改变了,但我同意你的看法,这是非常糟糕的设计.更好的设计可以看到用于只读功能的单独接口.

  • @Ivan:一个区别是它*有意义*并且*合理地实用*具有只读接口并保留可写接口到实际可写的实例.另一个区别是客户端代码在使用索引器之前检查数组边界是*相当实用*,但是必须调用"Add"来检查它是否抛出是完全荒谬的,只会导致错误的代码.(`IsReadOnly`属性不可靠:`((IList)new byte [0]).IsReadOnly`返回`false`,但是`((IList)new byte [0]).Add((byte)0)`仍然扔!) (2认同)

Iva*_*rić 9

虽然IList<T>接口定义Add(T)Insert(int,T)方法,但它也定义了IsReadOnly属性,如果你仔细阅读MSDN上的IList.Insert(int,T)IList.Add(T)方法的定义,你可以看到它们都指定NotSupportedException如果列表是只读.

说因为这个原因设计不好就像是说它设计也不好,因为当索引为负数或大于集合大小时Insert(int, T)可以抛出ArgumentOutOfRangeException.


Luk*_*keH 5

在我看来,这不是一个伟大的设计,而是必然的邪恶。

不幸的是,Microsoft没有在框架中包含IReadableList<>IWriteableList<>,并且IList<>自己实现了两者(或者甚至IList<>完全跳过并IWriteableList<>实现了IReadableList<>)。问题解决了。

但是现在进行更改为时已晚,如果您遇到一种情况,需要集合具有列表语义,并且您希望在运行时抛出异常而不是允许进行突变,那么ReadOnlyCollection<>不幸的是,这是您的最佳选择。


Kir*_*irk -3

我认为如果有一个糟糕的设计,那就是在不检查 ReadOnly 属性的情况下添加到 IList 的习惯。程序员忽略接口部分的习惯并不意味着接口很差。

事实上,我们程序员中很少有人会费心去阅读规范。说实话,对我来说,有很多事情比坐下来阅读整个规范文档更令人兴奋。(例如,看看一个人是否真的可以用牙签睁开眼睛。)此外,我还有一个限制,那就是我无论如何都不会记住所有事情。

话虽如此,在不至少查看属性和方法列表的情况下,不应使用接口。您认为名为“ReadOnly”的布尔属性的用途是什么?也许是因为该列表只能出于某种原因而被读取。如果您要获取从您自己的代码之外的某个地方传递的列表,您应该在尝试添加列表之前检查该列表是否是只读的。

  • 我不同意。该界面很差,因为它同时包含 Add 和 ReadOnly。一个好的设计是 IReadOnlyList,ReadOnlyCollection 将实现它并让 IList 继承自 IReadOnlyList。将不再需要 ReadOnly bodge 属性,并且任何实现 IList 的东西都将支持 Add 等。 (4认同)