代码合同:为什么有些不变量不在课堂外考虑?

时间:2010-07-29 14:19:52

标签: c# .net static-analysis code-contracts design-by-contract

考虑这种不可变类型:

public class Settings
{
    public string Path { get; private set; }

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

    public Settings(string path)
    {
        Contract.Requires(path != null);
        Path = path;
    }
}

这里有两点需要注意:

  • 有一个合约不变量,可确保Path属性永远不会null
  • 构造函数检查path参数值以尊重先前的合约不变量

此时,Setting实例永远不会拥有null Path属性。

现在,看看这种类型:

public class Program
{
    private readonly string _path;

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

    public Program(Settings settings)
    {
        Contract.Requires(settings != null);
        _path = settings.Path;
    } // <------ "CodeContracts: invariant unproven: _path != null"
}

基本上,它有自己的契约不变量(在_path字段上),在静态检查期间无法满足(参见上面的评论)。这对我来说听起来有点奇怪,因为它很容易证明:

  • settings是不可变的
  • settings.Path不能为null(因为“设置”具有相应的合约不变量)
  • 因此,通过将settings.Path分配给_path_path不能为空

我在这里错过了什么吗?

2 个答案:

答案 0 :(得分:10)

检查code contracts forum后,我发现了this similar question,其中一位开发人员给出了以下答案:

  

我认为您遇到的行为是由正在进行的某种方法间推断引起的。静态检查器首先分析构造函数,然后分析属性,然后分析方法。在分析Sample的构造函数时,它不知道msgContainer.Something!= null,因此它会发出警告。解决它的方法是在构造函数中添加一个假设msgContainer.Something!= null,或者更好地将postcondition!= null添加到Something。

换句话说,您的选择是:

  1. 使Settings.Path属性显式而不是自动,并在支持字段中指定不变量。为了满足您的不变量,您需要为属性的set访问器添加前置条件:Contract.Requires(value != null)

    您可以选择使用Contract.Ensures(Contract.Result<string>() != null)向get访问者添加后置条件,但静态检查器不会以任何方式投诉。

  2. Contract.Assume(settings.Path != null)添加到Program类的构造函数中。

答案 1 :(得分:0)

不变量不在私人会员身上工作,你实际上无法理解为什么会这样,希望这会有所帮助。