有许多信息表明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
}
}
答案 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