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

Liskov代换原理、前提和抽象方法

杨起运
2023-03-14

Liskov替代原理(LSP)说:

先决条件不能在子类型中得到加强。

在C#中,我可能违反以下整个原则:

public class A 
{
      public virtual void DoStuff(string text)
      {
            Contract.Requires(!string.IsNullOrEmpty(text));
      }
}

public class B : A
{
      public override void DoStuff(string text)
      {
            Contract.Requires(!string.IsNullOrEmpty(text) && text.Length > 10);
      }
}

但是,如果A.DoStuff是一种abstract方法,会发生什么呢

public class A 
{
      public abstract void DoStuff(string text);
}

public class B : A
{
      public override void DoStuff(string text)
      {
            Contract.Requires(!string.IsNullOrEmpty(text));
      }
}

现在A.DoStuff是无合同的。或者它的合同就是一切允许的。

那么,B. DoStuff前提条件是否违反了Liskov替代原则?

共有3个答案

羊城
2023-03-14

我认为不是。根据定义,抽象方法没有先决条件,因为没有实现。如果实现接口会破坏LSP,这与争论是一样的。

A. Do某物()是无契约的是一个不真实的前提。A. Do某物()是未定义的,因此它不能有超过其签名的契约。

岳均
2023-03-14

是的,你可以很容易地打破这个原则,不仅仅是在C。

它只指出:

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

在您的示例中,类型B不满足提供处理短文本的方法DoStuff的属性,尽管它的超类型a满足该属性。所以这个原则被违反了。

程序员应该坚持这个原则。属性也可以是“它做正确的事情”,您可以很容易地通过使用带有错误方法实现的子类型来打破它。

壤驷文华
2023-03-14

这取决于合同的定义。

LSP是一种理论构造,它不依赖于特定的语言或实现,例如C#的“代码契约”特性。

合同的定义如下:

  • 方法名称
  • 方法参数名称
  • 方法注释
  • 返回类型和方法参数类型
  • “明确”合同,如合同。需要

最后两个将由编译器验证。然而,前三个也可以是合同的一部分!考虑以下示例:

public interface StuffContainer
{
    void Add(string text);

    // Removes a string that has previously been added.
    void Remove(string text);
}

Remove方法的名称和文档定义了一个明确的先决条件。在实现中验证要删除的字符串之前已添加不会违反LSP。验证字符串是否至少包含5个字符将违反LSP。

 类似资料:
  • 我在派生类中重写了带有附加先决条件的虚拟函数。这是快照- 如果我理解正确的话,这里的代码通过附加一个先决条件打破了Liskov替换-IsImmediateProcess和其他日期检查。对吗?或者一个被重写的函数调用一个基函数,然后向它添加自己的行为,这样可以吗? 我不能将重写方法中由初始过程类型引入的条件移动到基本类型,因为它是特定于初始过程的。 在这种情况下,如果派生类重写行为并希望在不违反Li

  • 存在无法写入或查找的流派生类这一事实是否违反了Liskov替换原则? 例如,无法查找NetworkStream,如果调用方法,它将抛出。 还是因为存在标志就可以了? 考虑到众所周知的继承自的例子...将标志和添加到是否可以解决问题? 这难道不是打开了通过添加旗帜来解决问题的大门吗?

  • 每个类都直接或间接地继承自类。 类有一个重要的方法,最常被重写:。 问题是:对于类,重写此方法是否会违反Liskov替换原则? 我举个例子。 显然,如果我用替换,系统的行为就会改变。

  • 我试图理解Liskov替换原理,我有以下代码: 我不确定这是否违反了它。原理是,如果你有一个类S的对象,那么你可以用另一个类T的对象来代替它,其中S是T的一个子类。但是,如果我写了 这当然会产生编译错误,因为Vehicle类没有openDoor()方法。但这意味着我不能用它们的父类Vehicle替换VehicleWithDoors对象,这似乎违反了原则。那么这个代码是否违反了它?我需要一个好的解释

  • 我试图通过反复阅读维基百科条目来确定我对上述原则的理解。 撇开仍然让我悲伤的协变和逆变的概念不谈,wikipedia还提到超类型的不变量必须保留在子类型和历史约束或历史规则中。基于最后两个概念,我提出了一个小例子: 所以我的问题是:基于上述两个概念,我用这个例子是否违反了原则?若否,原因为何? 事先非常感谢。