c# - Liskov 替换原则与普通继承有何不同?

标签 c# inheritance liskov-substitution-principle

我想了解 Liskov 替换原则。但我无法确定 Liskov 替换原则与正常继承有何不同。 下面的代码是关于普通继承的。我应该如何处理下面的代码才能说我的代码遵循 Liskov 替换原则

    public class ClassA
    {
        public virtual void MethodA()
        {
            Console.WriteLine("-----------------ClassA.MethodA---------------------");
        }

        public virtual void MethodB()
        {
            Console.WriteLine("-----------------ClassA.MethodB---------------------");
        }
    }

    public class ClassB: ClassA
    {
        public override void MethodA()
        {
            Console.WriteLine("-----------------ClassB override ClassA.MethodA---------------------");
        }
    }

最佳答案

这是一个常见的定义:

https://www.tomdalling.com/blog/software-design/solid-class-design-the-liskov-substitution-principle/

The Liskov Substitution Principle (LSP): functions that use pointers to base classes must be able to use objects of derived classes without knowing it.

这里有一个更严谨的解释:

https://en.wikipedia.org/wiki/Liskov_substitution_principle

..Liskov 替换原则 (LSP) 是子类型关系的特定定义,称为(强)行为子类型,最初由 Barbara Liskov 在 1987 年题为数据抽象和层次结构的 session 主题演讲中引入。它是一种语义关系,而不仅仅是句法关系,因为它旨在保证层次结构中类型(尤其是对象类型)的语义互操作性。 Barbara Liskov 和 Jeannette Wing 在 1994 年的一篇论文中简要描述了该原理如下:

子类型要求:

设 ϕ ( x ) 是关于类型 T 的对象 x 的可证明属性。 那么对于类型 S 的对象 y 来说 ϕ ( y ) 应该为真,其中 S 是 T 的子类型。

Liskov 原则对更新的面向对象编程语言中采用的签名施加了一些标准要求(通常在类级别而不是类型级别;请参阅名义与结构子类型以了解区别):

  • 子类型中方法参数的逆变。
  • 子类型中返回类型的协方差。
  • 子类型的方法不应抛出新异常,除非这些异常本身是父类(super class)型方法抛出的异常的子类型。

除了签名要求外,子类型还必须满足一些行为条件。这些在类似于契约设计方法的术语中进行了详细说明,从而导致对契约如何与继承交互的一些限制:

  • 不能在子类型中加强先决条件。
  • 不能在子类型中弱化后置条件。
  • 父类(super class)型的不变量必须保留在子类型中。
  • 历史约束(“历史规则”)。对象被认为只能通过它们的方法(封装)进行修改。因为子类型可能会引入父类(super class)型中不存在的方法,所以这些方法的引入可能会允许子类型中的状态更改,而这在父类(super class)型中是不允许的。历史约束禁止这样做。

问:你的例子符合吗?

答:我不这么认为。因为 A.MethodA() 在语义上与 B.MethodA() “不同”。 B 类未通过 The Duck Test .

我提出这个反例:

public class ClassA {
  public virtual void MethodA() {
    Console.WriteLine("ClassA.MethodA");
  }

  public virtual void MethodB(){
    Console.WriteLine("ClassA.MethodB");
  }
}

public class ClassC: ClassA {
  public void MethodC() {
    Console.WriteLine("ClassC.MethodC");
  }
}

这也是为什么 LSP “等同于”继承的一个很好的例子。

关于c# - Liskov 替换原则与普通继承有何不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58897704/

相关文章:

c# - Nancy 模型绑定(bind)到子类

C++ 派生类 - 从父类调用虚函数?

java - Java 中隐藏名称的误导性示例

scala - 对不可变集合使用上限

java - Liskov 的替换原理与 JAVA 中的覆盖有何关系?完全阻碍和覆盖有什么区别?

C# Listview 添加带有图像和文本的项目,并将文本左对齐

c# - 如何更改所有控件的 MetroFramework 样式颜色

c# - 隐藏()和可见之间的控制差异?

java - 如何在java中调用super之前运行一个函数?

c++ - 为什么这在 C++ 构造函数中不起作用