代码合同 - ForAll - 静态验证支持什么

时间:2015-05-29 13:22:25

标签: c# static-analysis code-contracts

有许多信息表明Contract.ForAll的静态检查只有有限或没有支持。

我做了很多实验,发现可以使用

  • Contract.ForAll(items, i => i != null)
  • Contract.ForAll(items, p)其中p的类型为Predicate<T>

无法使用:

  • 现场访问
  • 物业访问
  • 方法组(我认为委托在此分配)
  • 实例方法调用

我的问题是:

  • ForAll可以使用的其他类型的代码是什么?
  • 代码合同是否承诺在Contract.ForAll(items, i => i != null)被证明之后,当在代码中稍后从列表中取出一个项目(即通过索引)时,该项目不为空?

这是完整的测试代码:

public sealed class Test
{
    public bool Field;
    public static Predicate<Test> Predicate;

    [Pure]
    public bool Property
    {
        get { return Field; }
    }    

    [Pure]
    public static bool Method(Test t)
    {
        return t.Field;
    }

    [Pure]
    public bool InstanceMethod()
    {
        return Field;
    }

    public static void Test1()
    {
        var items = new List<Test>();
        Contract.Assume(Contract.ForAll(items, i => i != null));
        Contract.Assert(Contract.ForAll(items, i => i != null)); // OK
    }

    public static void Test2()
    {
        var items = new List<Test>();
        Contract.Assume(Contract.ForAll(items, Predicate));
        Contract.Assert(Contract.ForAll(items, Predicate)); // OK
    }

    public static void Test3()
    {
        var items = new List<Test>();
        Contract.Assume(Contract.ForAll(items, i => i.Field));
        Contract.Assert(Contract.ForAll(items, i => i.Field)); // assert unproven
    }

    public static void Test4()
    {
        var items = new List<Test>();
        Contract.Assume(Contract.ForAll(items, i => i.Property));
        Contract.Assert(Contract.ForAll(items, i => i.Property)); // assert unproven
    }

    public static void Test5()
    {
        var items = new List<Test>();
        Contract.Assume(Contract.ForAll(items, Method));
        Contract.Assert(Contract.ForAll(items, Method)); // assert unproven
    }

    public static void Test6()
    {
        var items = new List<Test>();
        Contract.Assume(Contract.ForAll(items, i => i.InstanceMethod()));
        Contract.Assert(Contract.ForAll(items, i => i.InstanceMethod()));// assert unproven
    }
}

2 个答案:

答案 0 :(得分:4)

我无法找到更多的工作表达式,事实上我发现即使Contract.ForAll(items, i => i != null)也无法可靠地工作(但是它理解该项在以后在同一函数的foreach中使用时不为null)。最后,我放弃了使用静态检查器来使用更复杂的ForAll合同的可能性。

相反,我设计了一种方法来控制哪个合约用于静态检查器,哪些用于运行时检查器。我在这里介绍这种方法,希望它可能对原始问题中有趣的人有用。好处是能够编写更复杂的合同,这些合同只能在运行时检查,并且只能为静态检查器留下容易证明的合同(并且很容易将警告保持在低位数)。

为此,需要2个构建 Debug (如果您还没有), Debug Debug + Static Contracts Debug 构建定义了条件编译符号 MYPROJECT_CONTRACTS_RUNTIME 。通过这种方式,它会收到所有Contract.RtContract.合同。其他版本只收到Contract.份合同。

public static class RtContract
{
    [Pure] [ContractAbbreviator] [Conditional("MYPROJECT_CONTRACTS_RUNTIME")]
    public static void Requires(bool condition)
    {
        Contract.Requires(condition);
    }

    [Pure] [ContractAbbreviator] [Conditional("MYPROJECT_CONTRACTS_RUNTIME")]
    public static void Ensures(bool condition)
    {
        Contract.Ensures(condition);
    }

    [Pure] [Conditional("MYPROJECT_CONTRACTS_RUNTIME")]
    public static void Assume(bool condition)
    {
        Contract.Assume(condition);
    }
}

public class Usage
{
   void Test (int x)
   {
        Contract.Requires(x >= 0);  // for static and runtime
        RtContract.Requires(x.IsFibonacci());  // for runtime only
   }
}

答案 1 :(得分:1)

通过反编译mscorelib.dll System.Diagnostics.Contracts,我们可以轻松地看到如何构建Contract.ForAll:它需要收集和谓词。

public static bool ForAll<T>(IEnumerable<T> collection, Predicate<T> predicate)
{
    if (collection == null)
    {
        throw new ArgumentNullException("collection");
    }
    if (predicate == null)
    {
        throw new ArgumentNullException("predicate");
    }
    foreach (T current in collection)
    {
        if (!predicate(current))
        {
            return false;
        }
    }
    return true;
}

所以当你说Contract.ForAll(items, i => i.Field)时,i => i.Field是谓词

然后通过在所有测试方法中跟踪您的示例,我们可以看到您为Contract.ForAll方法提供了一个空列表,该列表将返回true,因为它永远不会进入foreach块。

进一步说明,如果您将项目添加到列表中 var items = new List<Test>() {new Test()};并再次运行测试,它将返回false,因为默认情况下public bool Field;为false

Contract.ForAll的目标是

  

确定集合中的所有元素是否都存在于   功能

所以我的结论是它不是关于Contarct.ForAll不能使用某些东西,而是你的集合中至少有一个元素返回false或为null