c# - 我可以获得代码契约(Contract)来警告我有关 "illegal"子类型的信息吗?

标签 c# compiler-warnings code-contracts invariants subtyping

抱歉,如果这个问题看起来太长了。在我提问之前,我需要说明它的来源。

设置:

给定以下不可变类型 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 不应该为 WidthHeight 设置独立的 setter

问题:

我如何处理代码契约,以便在我从可变 Rectangle 类派生 Square 时立即警告我违反契约? Code Contracts 的静态分析最好在编译时给我一个警告。

换句话说,我的目标是使用代码契约对以下规则进行编码:

  • WidthHeightRectangle 可以相互独立地改变。
  • Square
  • WidthHeight 不能相互独立地更改,这从一开始就没有意义。

... 并以这样一种方式进行,只要这些规则“冲突”,代码契约就会注意到。

到目前为止我考虑的是:

<强>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)); … }
}

这将禁止派生类 SquareWidth 更改时简单地将 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/

相关文章:

javascript - 如何在 JavaScript 中使用带有 ILIST<models> 的 foreach 循环?

java - 使用 Java JNA 调用 DLL 库

c# - 使用代码契约使泛型成为枚举类型

c# - 使用 CodeContracts 强制正确实现 INotifyPropertyChanged - "requires unproven"

html - F# 和静态检查联合案例

c# - Specflow 2022 生成步骤定义文件 - VSC BDD : All steps have been defined in this file already

c# - 我可以将多少个特定类的对象添加到列表中

iphone - '__strong' 仅适用于 objective-c 对象或 block 指针类型;这里输入的是 XXX”警告

Delphi禁用警告失败

c# - 异常过滤器触发 CA2202?