コード コントラクトを使用した配列ベースのスタックの次の部分的な実装を静的に検証しようとしています。メソッド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;
}
}