抱歉,如果这个问题看起来太长了。在我提问之前,我需要说明它的来源。
设置:
给定以下不可变类型 Rectangle
:
class Rectangle
{
public Rectangle(double width, double height) { … }
public double Width { get { … } }
public double Height { get { … } }
}
...从中派生类型 Square
似乎完全合法:
using System.Diagnostics.Contracts;
class Square : Rectangle
{
public Square(double sideLength) : base(sideLength, sideLength) { }
[ContractInvariantMethod]
void WidthAndHeightAreAlwaysEqual()
{
Contract.Invariant(Width == Height);
}
}
…因为派生类可以确保它自己的不变性永远不会被违反。
但是一旦我使 Rectangle
可变:
class Rectangle
{
public double Width { get; set; }
public double Height { get; set; }
…
}
…我不应该再从中派生 Square
,因为 Square
不应该为 Width
和 Height 设置独立的 setter
。
问题:
我如何处理代码契约,以便在我从可变 Rectangle
类派生 Square
时立即警告我违反契约? Code Contracts 的静态分析最好在编译时给我一个警告。
换句话说,我的目标是使用代码契约对以下规则进行编码:
Width
和Height
的Rectangle
可以相互独立地改变。Width
和Height
不能相互独立地更改,这从一开始就没有意义。
Square
的 ... 并以这样一种方式进行,只要这些规则“冲突”,代码契约就会注意到。
到目前为止我考虑的是:
<强>1。向 Rectangle
添加不变量:
class Rectangle
{
…
[ContractInvariantMethod]
void WidthAndHeightAreIndependentFromOneAnother()
{
Contract.Invariant(Width != Height || Width == Height);
}
}
这种方法的问题在于,虽然不变量正确地指出“宽度和高度不必相等,但它们可以”,但它是无效的,(1) 因为它是重言式,并且 (2 ) 因为它比派生类 Square
中的不变 Width == Height
限制更少。也许它甚至在 Code Contracts 看到它之前就被编译器优化掉了。
<强>2。向 Rectangle
的 setter 添加后置条件:
public double Width
{
get { … }
set { Contract.Ensures(Height == Contract.OldValue(Height)); … }
}
public double Height
{
get { … }
set { Contract.Ensures(Width == Contract.OldValue(Width)); … }
}
这将禁止派生类 Square
在 Width
更改时简单地将 Height
更新为 Width
,反之亦然相反,它不会阻止我本身从Rectangle
派生Square
类。但这是我的目标:让代码契约警告我 Square
不能从可变的 Rectangle
派生。
最佳答案
最近的 MSDN 杂志中有一篇非常相关的文章讨论了基本相同的问题:"Code Contracts: Inheritance and the Liskov Principle" - 我想不出更好的答案。
您认为“非法”子类型基本上违反了 Liskov 替换原则,这篇文章展示了代码契约如何帮助检测这个问题。
关于c# - 我可以获得代码契约(Contract)来警告我有关 "illegal"子类型的信息吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6976423/