【问题标题】:Is the virtual keyword inherently in-violation of the Liskov Substitution principle?虚拟关键字本质上是否违反了里氏替换原则?
【发布时间】:2021-11-29 07:43:08
【问题描述】:

Liskov 替换原则指出,您应该编写类继承,以便将子类型交换为其基类型不会因此而改变应用程序的行为。

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

【问题讨论】:

  • wont change the behavior of your application by doing so. 不,它没有这么说。你从哪里读到这个定义的?
  • @mjwills 是我对原理的理解,貌似有缺陷

标签: c# virtual design-principles liskov-substitution-principle


【解决方案1】:

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

引用Wikipedia:

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

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

其次,如果你的超类有一个virtual 方法,而子类重写了它让它退出程序,这是否违反了LSP?仅当调用者无法在超类的方法上假设时。

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

【讨论】:

    【解决方案2】:

    不幸的是,“这取决于”。

    Liskov 替换原则 (LSP) 通常是未定义的。它应该采用类型可替代性的概念(在类型理论意义上)并将其扩展到行为领域。也就是说,子类型的行为必须“扩展”超类型的行为。问题在于,它没有很好地定义这些行为是什么,以及如何测试是否要更改它们。

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

    例如采用 Clone() 方法。如果你有一个抽象类 Mammal 和一个子类 Human,那么在调用 Clone 时,Human 类必须返回一个 Human。这是因为 Clone() 方法的预期行为在英文单词“clone”的使用中被编纂。返回哺乳动物违反了预期的行为。

    虽然在 C# 中不可能正确实现 Clone() 方法,但 virtual 关键字可以实现“足够接近”的近似值,因此 virtual 不仅不违反 LSP,而且如果您使用基类,这是一个要求.

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

    【讨论】:

      【解决方案3】:

      引用我自己的话:

      不应该传递一个对象的继承者来代替基类 破坏被调用方法中的任何现有功能。你应该 能够用每个替换给定接口的所有实现 其他。

      在此处阅读我对 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 关键字在方法上使用时更糟糕;见上面的文章。

      【讨论】:

        猜你喜欢
        • 2015-02-15
        • 2017-05-14
        • 1970-01-01
        • 1970-01-01
        • 2017-07-26
        • 1970-01-01
        • 2015-01-01
        • 1970-01-01
        • 2012-01-12
        相关资源
        最近更新 更多