我正在尝试更详细地理解代码合同。我有以下设计的例子,我试图断言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
如果我使用冗长的方式来表达上面详述的内容,我只能使用代码契约进行完全干净的编译。我的问题是为什么!
答案 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;