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

在构造函数中是否可能违反Liskov替换原则?

龙嘉誉
2023-03-14

我刚刚安装了Microsoft代码合同。这是的一部分。NET框架和Visual Studio加载项。它提供运行时检查和定义的合同的静态检查。

该工具有四个警告级别,因此我设置了最高级别。

我已经声明了违反Liskov替换原则的类。

public class Person
{
    protected int Age { get; set; }

    public Person(int age)
    {
        Contract.Requires(age > 0);
        Contract.Requires(age < 130);
        this.Age = age;
    }
}

public class Child : Person
{
    public Child(int age) : base(age)
    {
        Contract.Requires(age > 0); 
        Contract.Requires(age < Consts.AgeOfMajority);
        Contract.Requires(age < 130);
        this.Age = age;
    }
}

public static class Consts
{
    public readonly static int AgeOfMajority = 18;
}

LSP规定:

如果S是T的子类型,则T类型的对象可以替换为S类型的对象,而不改变该程序的任何期望属性

在我的例子中,违规行为是这样的:人=新孩子(23);。我们应该能够做到这一点,但是我们不能,因为孩子们不能比个人班级要求的年龄小。

然而,分析的结果是令人惊讶的代码契约:检查了11个断言: 11个正确的。我的例子是错误的还是代码合同没有检测到这样的事情?

共有3个答案

冷浩瀚
2023-03-14

我认为liskov替换控制类的构造实例的行为。因此,一个适当构造的儿童实例可以代替一个没有问题的人。

您对如何构造一个孩子有限制。我不认为框架没有将此标记为问题。

云镜
2023-03-14

有一个著名的鸭子违反LSP的例子:

然而,我们不能在构造函数中违反它。假设我们有鸭子和野鸭课程:

public abstract class Duck
{
    public abstract string Quack();
    public double Weight { get; set; }

    public Duck(double weight)
    {
        Contract.Requires(weight > 0);
        this.Weight = weight;
    }
}

public class WildDuck : Duck
{
    public WildDuck(double weight)
        : base(weight)
    {
        Contract.Requires(weight > 0);
        this.Weight = weight;
    }

    public override string Quack()
    {
        return "wild quack";
    }
}

现在让我们来介绍一下:

public class ElectricDuck : Duck
{
    public Battery Battery { get; set; }

    public override string Quack()
    {
        return "electric quack";
    }

    public ElectricDuck(double weight, Battery battery)
        : base(weight)
    {
        Contract.Requires(weight > 0);
        Contract.Requires(battery != null);
        this.Weight = weight;
        this.Battery = battery;
    }
}

public class Battery
{
    public bool IsEmpty { get; set; }
}

乍一看,它似乎违反了LSP,因为电子鸭需要比野鸭或抽象鸭更多的东西。但这不是真的,只要电鸭提供嘎嘎方法没有额外的要求。

如果电鸭需要电池发光——从LSP的角度来看,这是完全正确的:

public void Glow()
{
    Contract.Requires(!this.Battery.IsEmpty);
}

当我们向继承的方法添加需求时,违反了LSP:

public override string Quack()
{
    Contract.Requires(!this.Battery.IsEmpty);
    return "electric quack";
}

此修改将导致CodeContracts显示警告。

宋劲
2023-03-14

虽然LSP确实指定了子类型,但不能对方法设置更严格的前提条件,但这不适用于构造函数,因为您没有以多态方式使用构造函数。

违反合同的将是新生儿(23) 在分配给人员之前发生。

因此,示例冲突是错误的,它没有创建子类型的实例,更不用说将其替换为t了。

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

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

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

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

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

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