我对包含代码合同的以下代码有一些警告。
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>())
两个问题:
我的后置条件声明x&gt; = Contract.Result(),但“确保未经证实”警告状态x&gt; Contract.Result()。 (更好或相等与伟大)这怎么可能发生?
为什么不能在上面的陈述中证明set.Any()?
提前谢谢。
答案 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)。