代码契约:IEnumerable.Min上的静态检查失败

时间:2012-12-12 11:33:28

标签: c# code-contracts

我对包含代码合同的以下代码有一些警告。

public static int Min(IEnumerable<int> set)
{
    Contract.Requires(set != null);
    Contract.Requires(set.Any());

    Contract.Ensures(Contract.ForAll(set, x => x >= Contract.Result<int>()));

    int min = set.Min();

    return min;
}

static void Main(string[] args)
{
    Console.WriteLine(Min(new int[] {3,4,5}));
    Console.WriteLine(Min(new int[] {})); // should fail
}

我收到以下警告:

Requires unproven: set.Any() on Min(new int[] {3,4,5})

Ensures unproven: Contract.ForAll(set, x => x > Contract.Result<int>())

两个问题:

  1. 我的后置条件声明x&gt; = Contract.Result(),但“确保未经证实”警告状态x&gt; Contract.Result()。 (更好或相等与伟大)这怎么可能发生?

  2. 为什么不能在上面的陈述中证明set.Any()?

  3. 提前谢谢。

2 个答案:

答案 0 :(得分:1)

Ensures子句对所有IEnumerable开头无效。您可以编写IEnumerable,在第一次枚举时返回一个序列(例如1, 2, 3),第二次返回另一个列表(例如0)。它是具有任意实现的接口。

IEnumerable通常会有一些很多(可能生成的)内容。我不认为CC可以看透,即使具体的运行时类型以某种方式已知。

CC是否能够启发式推理IEnumerable?那对我来说是新的。它必须假设一个序列在多次枚举时不会改变(在数据库查询的情况下这是非常错误的。)

让我指出一个主观的侧面说明,我发现CC检查器太有限,无法使用。它会导致疯狂的麻烦,以证明有趣的属性。它不能很好地应对抽象。

答案 1 :(得分:0)

STATIC代码检查器无法确定该集合包含任何数据。如果你考虑一下,检查器不知道各种方法和属性的作用,除非在方法本身内定义了一些契约。

在您的情况下,检查器不知道扩展方法Any()或Min()的结果可能是什么,因此它无法验证任何Requires和Ensures。

通过更改参数的类型可以减少警告,但最后代码检查程序仍然无法确保您的代码能够满足Min(...)的要求。

如果将类型更改为int []或List,则某些警告可能会消失。以下代码不返回警告:

    public static int Min(List<int> set)
    {
        Contract.Requires(set != null);
        Contract.Requires(set.Count>0);


        Contract.Ensures(Contract.ForAll(set, x => x >= Contract.Result<int>()));

        int min = set.Min();

        return min;
    }

    static void Main(string[] args)
    {

        Console.WriteLine(Min(new List<int> { })); // should fail
        Console.WriteLine(Min(new List<int> { 3, 4, 5 }));
        Console.ReadKey();
    }

当然,如果您运行此代码,它将失败并出现异常,因此代码检查器可能足够智能,可以检测到这一点。

如果您保留原始订单,则仍会收到警告:

        Console.WriteLine(Min(new List<int> { 3, 4, 5 }));
        Console.WriteLine(Min(new List<int> { })); // should fail

在这两种情况下,您也会收到一些疯狂的建议,例如在Main()中添加Contract.Ensures(false)。