繁体   English   中英

Liskov替换原理和接口

[英]Liskov substitution principle and Interface

ICollection<T>.Add() - 数组的实现是否违反了Liskov替换原则? 该方法导致NotSupportedException ,它会破坏LSP,恕我直言。

string[] data = new string[] {"a"};
ICollection<string> dataCollection = data;
dataCollection.Add("b");

这导致了

未处理的异常:System.NotSupportedException:Collection具有固定大小。

我发现了一个关于Stream -implementations的非常类似的问题。 我打开一个单独的问题,因为这个案例非常不同: Liskov替换原则和Streams 这里的区别在于ICollection不像Stream CanAdd那样提供CanAdd -Property或类似的东西。

我明白你为什么这么认为。 有一个函数需要一个集合,它期望它是可修改的。 传递数组会使它失败,所以很明显你不能用这个特定的实现替换接口,对吧?

这是个问题吗? 也许。 这取决于你期望理想持有的频率。 你是不是偶然会使用一个阵列而不是一个集合,然后在十年之后惊讶它会崩溃? 并不是的。 .NET应用程序使用的类型系统并不完美 - 它没有告诉您这种特定的ICollection<T>用法要求集合可以修改。

如果数组没有假装实现ICollection<T> (或IEnumerable<T> ,它们也没有“真正”实现),.NET会更好吗? 我不这么认为。 有没有办法保持数组的“方便性” ICollection<T> ,这也可以避免相同的LSP违规? 不。 底层数组仍然是固定长度的 - 充其量,你会违反更多有用的原则(比如预期引用类型不具有引用透明性这一事实)。

可是等等! 让我们看一下ICollection<T>.Add的实际合约。 是否允许抛出NotSupportedException 哦是的 - 引用MSDN:

[如果......则抛出NotSupportedException] ICollection是只读的。

当您查询IsReadOnly时,数组会返回true。 合同得到维护。

如果您认为Stream不会因为CanWrite而破坏LSP,则必须将数组视为有效集合,因为它们具有IsReadOnly ,并且它是true 如果函数接受只读集合并尝试添加它,则该函数中存在错误。 没有办法在C#/ .NET中明确指定它,所以你必须依赖合同的其他部分而不仅仅是类型 - 例如,函数的文档应该指定为集合抛出NotSupportedException (或ArgumentException或其他)这是只读的。 一个好的实现将在函数开始时进行此测试。

需要注意的一件重要事情是,C#中的类型并不像定义LSP的类型理论那样受到限制。 例如,您可以在C#中编写这样的函数:

bool IsFrob(object bobicator)
{
  return ((Bob)bobicator).IsFrob;
}

bobicator可以用任何超类型的object代替吗? 显然不是。 但它显然不是可怜的Frobinate类型的问题 - 它是IsFrob函数中的错误。 在实践中,C#(和大多数其他语言)中的许多代码仅适用于比方法签名中的类型所指示的约束更多的对象。

如果对象违反了其超类型的合同,则该对象仅违反LSP。 它不能对其他代码违反LSP负责。 通常你会发现在LSP下完全没有完全支持的代码是非常务实的 - 工程是,而且一直都是关于权衡。 仔细权衡成本。

不,因为它不是一个类 - 接口和实现类之间的关系与super和subclass之间的关系不同。

LSP特别适用于暗示实现的代码行为 - 接口没有实现,因此LSP不适用。

但是,这违反了接口隔离原则 ,它说您应该编写接口以避免未实现的方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM