【发布时间】:2012-11-28 22:26:44
【问题描述】:
里氏替换原则要求子类型必须满足超类型的契约。据我了解,这意味着ReadOnlyCollection<T> 违反了 Liskov。 ICollection<T> 的合约暴露了Add 和Remove 操作,但是只读子类型不满足这个合约。例如,
IList<object> collection = new List<object>();
collection = new System.Collections.ObjectModel.ReadOnlyCollection<object>(collection);
collection.Add(new object());
-- not supported exception
显然需要不可变的集合。 .NET 的建模方式有问题吗?更好的方法是什么? IEnumerable<T> 在公开集合方面做得很好,至少看起来是不可变的。但是,语义非常不同,主要是因为IEnumerable 没有显式公开任何状态。
在我的特殊情况下,我正在尝试构建一个不可变的DAG 类来支持FSM。一开始我显然需要AddNode / AddEdge 方法,但我不希望一旦它已经运行就可以更改状态机。我很难表示 DAG 的不可变和可变表示之间的相似性。
现在,我的设计涉及预先使用 DAG Builder,然后创建一次不可变图形,此时它不再可编辑。 Builder 和具体的不可变 DAG 之间的唯一通用接口是 Accept(IVisitor visitor)。我担心面对可能更简单的选项,这可能是过度设计/过于抽象。同时,如果客户端获得特定实现,我无法接受我可以在我的图形接口上公开可能抛出NotSupportedException 的方法。处理此问题的正确方法是什么?
【问题讨论】:
-
@Jodrell Liskov 原则还指出子类中的方法不应抛出新异常。只有相同的异常或从父类的方法中抛出的异常派生的异常。
-
我同意:ReadOnlyCollection 违反了 LSP。
-
@Guillaume 谢谢,这是我今天的“今天我学到的”。
-
原则才能被打破。 :)
-
好吧,
IList<T>接口完整合同包括列表可以只读或不只读的事实,因为隐含的ICollection<T>.IsReadOnly属性。所以关于这个只读状态,我认为接口/继承合同本身并没有真正规定任何东西。换句话说,如果您是IList<T>,则在调用 Add 时可以随意抛出,前提是 IsReadOnly 返回 true。我同意这并不能真正回答你的问题:-)
标签: c# immutability directed-acyclic-graphs liskov-substitution-principle readonly-collection