21

我有以下情况:

public interface ISomething
{
    void DoStuff();
    //...
}

public class Something : ISomething
{
    private readonly ISomethingElse _somethingElse;
    //...

    public Something (ISomethingElse somethingElse)
    {
         Contract.Requires(somethingElse != null);
        _somethingElse = somethingElse;
    }

    public void DoStuff()
    {
        // *1* Please look at explanation / question below
        _somethingElse.DoThings();
    }
 }

在第1行并打开静态检查器,我会收到一条警告,说它_somethingElse可能为空,如果我添加合同,它会给我错误

[Type] 实现接口方法 {Interface.Method} 因此不能添加要求

在这里做的最好的事情是什么?我看到的选项包括

  1. 一个保护条款,虽然它看起来有点极端
  2. 一种Contract.Assume
  3. 我没有想到的隐藏的第三个选项

请注意,该字段readonly在构造函数中设置值后无法更改。因此,来自代码合同的警告似乎有点无关紧要。

4

1 回答 1

19

解释

第 3 节:用户手册合同继承规定所有先决条件必须在继承/实现链的根方法中定义:

如果客户端确保他们已经满足前提条件并且有一个o静态类型为的变量T,那么客户端在调用时不应该遇到前提条件冲突o.M。即使运行时值o具有 type ,这也必须为真U。因此,该方法U.M不能添加比 的前提条件更强的前提条件T.M

虽然我们可以允许一个较弱的前提条件,但我们发现这样做的复杂性超过了好处。我们只是还没有看到任何令人信服的例子来说明削弱前提条件是有用的。因此,我们根本不允许在子类型中添加任何先决条件。

因此,必须在继承/实现链的根方法上声明方法先决条件,即第一个虚拟或抽象方法声明,或接口方法本身。

解决方案

在您的情况下,最好的做法是设置一个不变量,说明该_somethingElse字段永远不会为空:

[ContractInvariantMethod]
private void ObjectInvariant() {
    Contract.Invariant(_somethingElse != null);
}

这当然总是正确的,因为该字段readonly在构造函数中被标记和初始化。静态检查器无法自行推断这一点,因此您必须通过该不变量明确告诉它。

您可以选择Contract.Ensures(_somethingElse != null);向构造函数添加后置条件,但静态检查器不需要它。

于 2010-08-05T12:50:54.850 回答