我正在尝试使用代码契约静态验证基于数组的堆栈的以下部分实现。方法Pop()
使用纯函数IsNotEmpty()
来确保后续数组访问将位于下限/上方。静态验证程序失败,并建议我添加前置条件Contract.Requires(0 <= this.top)
。
有人可以解释为什么验证者无法证明数组访问对于给定合同IsNotEmpty()
的下限有效吗?
起初我认为Contract.Requires(IsNotEmpty())
方法可能不正确,因为子类可以覆盖IsNotEmpty()
。但是,如果我将类标记为sealed
,则验证程序仍无法证明数组访问是有效的。
更新:如果我将IsNotEmpty()
更改为只读属性,则验证会按预期成功完成。这提出了一个问题:如何/为什么只读属性和纯函数处理不同?
class StackAr<T>
{
private T[] data;
private int capacity;
/// <summary>
/// the index of the top array element
/// </summary>
private int top;
[ContractInvariantMethod]
private void ObjectInvariant()
{
Contract.Invariant(data != null);
Contract.Invariant(top < capacity);
Contract.Invariant(top >= -1);
Contract.Invariant(capacity == data.Length);
}
public StackAr(int capacity)
{
Contract.Requires(capacity > 0);
this.capacity = capacity;
this.data = new T[capacity];
top = -1;
}
[Pure]
public bool IsNotEmpty()
{
return 0 <= this.top;
}
public T Pop()
{
Contract.Requires(IsNotEmpty());
//CodeContracts: Suggested precondition:
//Contract.Requires(0 <= this.top);
T element = data[top];
top--;
return element;
}
}
答案 0 :(得分:4)
这应该可以解决问题:
[Pure]
public bool IsNotEmpty() {
Contract.Ensures(Contract.Result<bool>() && 0 <= this.top || !Contract.Result<bool>() && 0 > this.top);
return 0 <= this.top;
}
有关详细信息,请参阅代码合同论坛中的此主题:Calling a method in Contract.Requires
修改强>
此问题的另一个解决方案,如上面链接的线程中所述,是-infer
命令行选项:
现在,推断这种方法的post条件是可能的,事实上我们可以选择这样做:尝试在合同属性窗格中的额外选项行添加-infer确保静态检查器。
我已经检查过,这确实解决了这个问题。如果查看code contracts manual,您会看到默认选项仅用于推断属性后置条件。通过使用此开关,您可以告诉它尝试推断方法的后置条件:
-infer(需要+ propertyensures + methodensures)(默认= propertyensures)