Contract.Ensures中的自定义方法

时间:2011-12-05 13:51:53

标签: c#-4.0 code-contracts

我正在尝试更详细地理解代码合同。我有以下设计的例子,我试图断言try / get模式的不变量,如果它返回true那么out对象是非空的,否则如果它返回false

    public static bool TryParseFruit(string maybeFruit, out Fruit fruit)
    {
        Contract.Requires(maybeFruit != null);

        Contract.Ensures((Contract.Result<bool>() && Contract.ValueAtReturn(out fruit) != null) ||
                         (!Contract.Result<bool>() && Contract.ValueAtReturn(out fruit) == null));

        // Elided for brevity
        if (ICanParseIt())
        {
            fruit = SomeNonNullValue;
            return true;
        }
        else
        {
            fruit = null;
            return false;
        }
    }

我不喜欢Contract.Ensures内的重复,所以我想为此考虑我自己的方法。

[Pure]
public static bool Implies(bool a, bool b)
{
   return (a && b) || (!a && !b);
}

然后我将TryParseFruit中的不变量更改为

Contract.Ensures(Implies(Contract.Result<bool>(), Contract.ValueAtReturn(out fruit) != null);

但这会产生“确保未经证实”的警告。如果我然后在我的Implies方法上执行内联重构,那么一切都会好的。

有人可以向我解释为什么会这样吗?我猜这是因为Contract.ValueAtReturn以某种方式神奇地使用,这意味着我不能将结果传递给另一个函数并期望它能够正常工作。

(更新#1)

我认为以下所有Contract.Ensures表示相同的事情(即如果函数返回true,则fruit为非空,否则fruit为{ {1}})。请注意,我一次只使用其中一个:)

null

但是,以上Contract.Ensures(Implies(Contract.Result<bool>(), Contract.ValueAtReturn(out fruit) != null)); Contract.Ensures(Contract.Result<bool>() == (Contract.ValueAtReturn(out fruit) != null)); Contract.Ensures(Contract.Result<bool>() ^ (Contract.ValueAtReturn(out fruit) == null)); 都没有导致下面代码的干净编译。我希望Code.Contracts表示Contract.Ensures不能是空引用。

fruit.Name

如果我使用冗长的方式来表达上面详述的内容,我只能使用代码契约进行完全干净的编译。我的问题是为什么!

2 个答案:

答案 0 :(得分:1)

首先,您可以在不使用自定义方法的情况下压缩您的情况:

Contract.Ensures(Contract.Result<bool>() ^ (Contract.ValueAtReturn(out fruit) == null));

(此处^XOR operator

现在谈谈你的问题。我认为除非你确切知道静态验证器的工作原理,否则很难说出原因是什么。可能有数百个限制。从我的角度来看,Code Contracts验证器会以某种方式停止在方法边界上。我的意思是验证者不会查看Implies方法,也不知道它的作用。因此,它无法在每种情况下得出它返回的内容。当您内联该方法时,它可以完全检查代码。但是,我认为开发团队之外没有人确切知道。

<强>更新

正如在评论中已经弄清楚的那样,XOR运算符似乎不受当前CodeContracts版本的支持。下次好运...

答案 1 :(得分:1)

当然,你也可以尝试Contract.Ensures(Contract.Result<bool>() == (Contract.ValueAtReturn(out fruit) != null));我有一个模糊的回忆,分析师喜欢==给其他运营商。

我用Contract.Assert跟踪这些事情取得了一些成功,这有助于您找出分析中的漏洞所在。我还发现Contract.Assert允许分析成功的情况。换句话说,您可以通过几个断言来解决这个问题:

public static bool TryParseFruit(string maybeFruit, out Fruit fruit) 
{ 
    Contract.Requires(maybeFruit != null); 

    Contract.Ensures(Implies(Contract.Result<bool>(), Contract.ValueAtReturn(out fruit) != null);

    // Elided for brevity 
    if (ICanParseIt()) 
    { 
        fruit = SomeNonNullValue; 
        Contract.Assert(Implies(Contract.Result<bool>(), Contract.ValueAtReturn(out fruit) != null);
        return true; 
    } 
    else 
    { 
        fruit = null; 
        Contract.Assert(Implies(Contract.Result<bool>(), Contract.ValueAtReturn(out fruit) != null);
        return false; 
    } 
} 

凌乱,我知道。另一方面,如果任何断言失败,您可以查看,例如,逻辑的其他方面。例如,您可以使用Contract.Assert(SomeNonNullValue != null);丢弃代码,以查看分析器在SomeNonNullValue的非归零性方面失去确定性的位置。

修改

如果Assert未经证实,但您知道它应该是可证明的,那么您可以使用它来帮助隔离问题。我怀疑问题(或至少部分问题)是Ensures方法中缺少Implies。尝试添加Contract.Ensures(Contract.Result<bool>() == (Contract.OldValue(a) == Contract.OldValue(b)));此外,由于我再次对不同逻辑运算符的不同处理进行了模糊的回忆,请尝试重新声明该方法的返回。例如:return a == b;