これを書くと:
public sealed class Foo
{
private int count;
private object owner;
private void Bar()
{
Contract.Requires(count > 0);
Contract.Ensures(owner == null || count > 0);
if (count == 1)
owner = null;
--count;
}
}
静的コントラクト チェッカーは、すべてのアサーションを証明できます。
しかし、代わりにこれを書くと:
public sealed class Foo
{
private int count;
private object owner;
private void Bar()
{
Contract.Requires(count > 0);
Contract.Ensures(owner == null || count > 0);
--count;
if (count == 0)
owner = null;
}
}
owner == null || count > 0
事後条件が証明されていないと主張しています。
2 番目の形式がこの事後条件に違反していないことを証明できると思います。
// { count > 0 } it's required
--count;
// { count == 0 || count > 0 } if it was 1, it's now zero, otherwise it's still greater than zero
if (count == 0)
{
// { count == 0 } the if condition is true
owner = null;
// { count == 0 && owner == null } assignment works
}
// { count == 0 && owner == null || count != 0 && count > 0 } either the if was entered or not
// { owner == null || count > 0 } we can assume a weaker postcondition
私の証明に何か問題がありますか?
証明にアサーションをContract.Assert
コードへの呼び出しとして追加し、これだけを追加すると事後条件を証明できるという結論に達しました。
--count;
Contract.Assert(count == 0 || count > 0)
if (count == 0)
owner = null;
しかし、同じアサーションを「より自然な」方法に変更すると、失敗します。
--count;
Contract.Assert(count >= 0)
if (count == 0)
owner = null;
これら 2 つのアサーションは同等であると予想されますが、静的チェッカーはそれらを異なる方法で扱います。
(ちなみにVS10のベータ2を使っています)