当前位置: 首页 > 知识库问答 >
问题:

virtual关键字是否本质上违反了Liskov替换原则?

令狐钧
2023-03-14

Liskov替换原则指出,您应该编写您的类继承,这样将子类型交换为它们的基类型就不会改变应用程序的行为。

然而,虚拟关键字字面上似乎存在,以允许子类型的行为不同于基类型。虚拟/覆盖关键字(不包括覆盖抽象成员)的大多数使用不可能违反Liskov吗?我觉得这可能比我理解的更微妙。也许这是一个“规则有时会被打破”的情况,或者原则中的“不要有不同的行为”部分有一个灰色地带。

共有3个答案

缪朝
2023-03-14

首先,LSP没有说子类型不能改变行为,它说子类型不能改变超类上假设的属性。

引用维基百科:

子类型要求:设(x)是一个关于类型为T的对象x的可证明的属性,那么(y)对于类型为S的对象y应该是真的,其中S是T的子类型。

也就是说,子类型可能不会改变Ф,其中Ф是您可以对超类型做出的任何假设(“可证明属性”)。

第二,如果您的超类有一个virtual方法,并且子类重写它以使其退出程序,那么它是否违反了LSP?只有在调用方不能在超类的方法上假定这一点时。

这意味着,可以很容易地使用虚拟方法编写超类,从而很容易违反LSP。但它不是虚拟的固有属性
然而,该语言可能做得更好的是强制virtual重写至少在其代码中的某个地方调用超类方法。

松阳泽
2023-03-14

不幸的是,“视情况而定”。

利斯科夫替代原则(LSP)通常未定义。它应该接受类型可替代性的概念(在类型理论意义上),并将其扩展到行为领域。也就是说,子类型的行为必须“扩展”超级类型的行为。问题在于,它没有很好地定义这些行为是什么,以及如果您正在改变这些行为,您可以如何进行测试。

有许多明显构成违反《法律和社会保障法》的例子。其中大多数都有缺陷,因为它们没有考虑到所有类型替换的先决条件,包括对接口中的方法和类型参数的所有有效的协变和逆变更改都是正确的。LSP是此类型检查的扩展。此外,在签名中使用这种差异的能力是正确实现接口的许多行为的要求,因此满足LSP。

例如,采用Clone()方法。如果您有一个抽象类哺乳类和一个子类Human,则调用Clone时Human类必须返回Human。这是因为在使用英语单词“Clone”时编码了Clone()方法的预期行为。将哺乳动物送回违反了预期的行为。

虽然在C#中无法正确实现Clone()方法,但virtual关键字可以实现“足够接近”的近似值,因此virtual不仅不违反LSP,而且在使用基类时也是必需的。

最后,与SOLID中的所有主体一样,LSP更多地是“代码气味”的集合,而不是实际的可定义规则。他们在那里给你一种讨论为什么代码不好的方式。它不会告诉您代码的真正问题是什么,也不会告诉您如何更正它或如何防止它发生。

松涵容
2023-03-14

引用我自己的话:

传递对象的继承者代替基类不应该破坏被调用方法中的任何现有功能。您应该能够相互替换给定接口的所有实现。

在这里阅读更多我对C#中Liskov替换的想法:https://docs.microsoft.com/en-us/archive/msdn-magazine/2014/may/csharp-best-practices-dangers-of-violating-solid-principles-in-csharp

virtual关键字本身并不是罪魁祸首。is关键字通常是次要Liksov违规的罪魁祸首,因为人们使用它来检查他们的活动实例是否是某个继承者,从而确保行为根据继承而改变。在方法上使用的new关键字更差;见上述文章。

 类似资料:
  • 我是OOP的新手。最近我读到了关于Liskov替换原理的文章。 在下面给出的代码中,Square类继承Give_区域。假设Square类与Square相关(比如有效性检查)。Give_Area给出正方形的面积(4个顶点位于圆的周长上)和圆的面积。所以,如果给我一个半径,我必须打印圆和正方形的面积(由放置在圆周长上的顶点组成)。为了得到圆的面积,我使用了一个参数。但在求平方面积时没有参数。因此,我在

  • 我正在阅读为什么Java中的数组协方差不好(为什么数组是协方差的,而泛型是不变的?)。如果是的子类型,则是的子类型。这是一个问题,因为这样的事情是可以做的: 这与“正确”实现的泛型不同。不是的子类型 我试图理解为什么它是坏的本质,并且刚刚读了关于LSP的文章。它有没有违反LSP?似乎没有明显的违规行为。

  • 有人能告诉我下面的例子是否违反了LSP吗? 我有一个例子: 和子类: 和主类: 在此示例中,子类添加名为 的新属性,并通过对其自己的属性 进行附加检查来覆盖方法。 在main方法中,我创建了2个对象。第一个是类型的对象,第二个是类型的对象。 当验证人员时,因为所有前提条件都是正确的,所以它是正确的,但是对于员工,它将抛出< code > IllegalArgumentException ,因为它与

  • 来自维基百科, Liskov的行为子类型概念定义了对象的可替代性概念;也就是说,如果S是T的子类型,则程序中T类型的对象可以替换为S类型的对象,而不改变该程序的任何期望属性(例如正确性)。 假设以下类层次结构: 基本抽象类-。它有一个只读属性,在后继程序中被重写。 基类的继承者-,它重写并返回灰色。 Cat的继任者-,它覆盖并返回带条纹的。 然后我们声明一个方法,参数类型为(不是)。 向该方法发送

  • 我有以下代码: 所以我们知道

  • 如果S是T的一个子类型,那么T类型的对象可以被S类型的对象替换。 子类有两种不同的行为(选中与未选中),在某些情况下,除非更改当前代码,否则无法用子类对象有效地替换基类用法,例如,如果编写如下代码: 这是违反吗?,为什么/为什么不?。 资料来源:http://www.oracle.com/technetwork/articles/entarch/effective-exceptions-09234