我刚刚安装了Microsoft Code Contracts。它是.NET Framework和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类型的对象没有改变任何理想的属性 该计划
在我的示例中,违规将是此作为:Person person = new Child(23);
。我们应该能够做到这一点,但我们不能,因为孩子不能比某个年龄小于人类所要求的年龄。
然而,分析结果令人惊讶CodeContracts: Checked 11 assertions: 11 correct
。我的示例是错误的还是Code Contracts没有检测到这样的事情?
答案 0 :(得分:15)
虽然LSP指定子类型不能在方法上设置更多限制性前提条件,但这并不适用于构造函数,因为您不以多态方式使用构造函数。
合同违规将new Child(23);
在分配到Person
之前发生。
因此,示例违规是错误的,它不会创建子类型S的实例,更不用说将其替换为T.
答案 1 :(得分:6)
有一个着名的LSP违规示例:
然而,我们不能在构造函数中违反它。假设我们有Duck和WildDuck类:
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";
}
}
现在让我们介绍一下ElectricDuck:
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,因为ElectricDuck需要的不仅仅是WildDuck或抽象Duck。但只要ElectricDuck提供Quack方法而没有其他要求,它就不是真的。
如果ElectricDuck要求电池发光 - 从LSP的角度来看,这是完全正确的:
public void Glow()
{
Contract.Requires(!this.Battery.IsEmpty);
}
当我们将要求添加到继承方法时违反了LSP:
public override string Quack()
{
Contract.Requires(!this.Battery.IsEmpty);
return "electric quack";
}
此修改将导致CodeContracts显示警告。
答案 2 :(得分:2)
我想说liskov替换会控制构造的类实例的行为。因此,正确构造的Child实例可以替代没有问题的Person。
您对如何构建Child有约束。我没有看到框架没有将此标记为问题。