问题中的所有四个项目都已在this article 中进行了彻底审查。
先决条件不能在子类型中得到加强。
This answer 给出了“真鸭”和“电鸭”的例子,建议你去看看。为简洁起见,我将在本项目中使用它。
这意味着子类型不能妨碍原始方法在基类中的行为方式。在上面提到的答案代码中,两只鸭子都可以游泳,但ElectricDuck 只有在打开时才会游泳。因此,任何需要鸭子(来自接口IDuck)游泳的代码单元现在都不起作用,除非明确指定鸭子是ElectricDuck(然后打开),这需要实现无处不在。
后置条件不能在子类型中被削弱。
对于这个,我们可以从鸭子的类比中退后一步。让我们以this 答案为基础。假设我们有一个只接受正整数的基类。如果在子类型中,在扩展方法时,我们删除了数字必须为正的条件,那么过去认为该数字为正的所有代码单元现在都有被破坏的风险,因为现在无法保证这个数字是正数。这是这个想法的一个表示:
public class IndexBaseClass
{
protected int index;
public virtual int Index
{
get
{
//Will return positive integers only
return index < 0 ? 0 : index;
}
set
{
index = value;
}
}
}
public class IndexSubClass : IndexBaseClass
{
public override int Index
{
get
{
//Will pay no mind whether the number is positive or negative
return index;
}
}
}
public class Testing
{
public static int GetIndexOfList(IndexBaseClass indexObject)
{
var list = new List<int>
{
1, 2, 3, 4
};
return list[indexObject.Index];
}
}
如果我们调用 GetIndexOfList 传递一个 IndexSubClass 对象,则不能保证该数字是正数,因此可能会破坏应用程序。想象一下,您已经在整个代码中调用了这个方法。您必须浪费时间检查所有实现中的正值。
超类型的不变量必须保存在子类型中。
父类可能有一些不变量,即只要对象存在,一些条件就必须保持为真。任何子类都不应该继承该类并消除这个不变量,因为到目前为止所有实现都有崩溃的风险。在下面的例子中,如果它是负数,父类抛出一个异常然后设置它,但子类只是简单地忽略它,它只是设置不变量。
以下代码取自here:
public class ShippingStrategy
{
public ShippingStrategy(decimal flatRate)
{
if (flatRate <= decimal.Zero)
throw new ArgumentOutOfRangeException("flatRate", "Flat rate must be positive
and non-zero");
this.flatRate = flatRate;
}
protected decimal flatRate;
}
public class WorldWideShippingStrategy : ShippingStrategy
{
public WorldWideShippingStrategy(decimal flatRate)
: base(flatRate)
{
//The subclass inherits the parent's constructor, but neglects the invariant (the value must be positive)
}
public decimal FlatRate
{
get
{
return flatRate;
}
set
{
flatRate = value;
}
}
}
历史约束(“历史规则”)。
这条和上一条规则一样。它指出子类型不应引入改变父类中的不可变属性的方法,例如将子类中的新Set方法添加到曾经的属性只能通过构造函数设置。
一个例子:
public class Parent
{
protected int a;
public Parent(int a)
{
this.a = a;
}
}
public class Child : Parent
{
public Child(int a) : base(a)
{
this.a = a;
}
public void SetA(int a)
{
this.a = a;
}
}
现在,由于子类,父类中以前不可变的属性现在是可变的。这也违反了 LSP。