我有一个像这样开始的方法:
public static UnboundTag ResolveTag(Type bindingType, string name, string address)
{
Contract.Requires(bindingType != null);
var tags = GetUnboundTagsRecursively(bindingType).ToArray();
实现GetUnboundTagsRecursively的合同(在同一个类中实现)如下所示:
public static IEnumerable<UnboundTag> GetUnboundTagsRecursively(Type bindingType)
{
Contract.Requires(bindingType != null);
Contract.Ensures(Contract.Result<IEnumerable<UnboundTag>>() != null);
静态分析器指示ResolveTag的标记分配行上的消息"requires unproven: source != null"
。我已经好好看了好几次,但我无法弄清楚为什么会这样。我假设这是对扩展方法source
的{{1}}参数的引用。我的方法声明它确保结果不为null,因此这似乎意味着ToArray()
的源也不为null。我错过了什么?
附加信息:返回IEnumerable的方法是使用yield return调用实现的。我想知道是否可能是枚举器魔法以某种方式搞乱代码合同......
我只是尝试更改实现以返回一个空数组,而不是使用yield return并传递合同,所以显然这是使用yield return的方法的问题。有人知道解决这个问题吗?
我看了一下IL for Contracts DLL,它实际上是在MoveNext()中为调用器实现调用了契约:
ToArray()
这很有趣,因为Contract.Result调用实际上使用了错误的类型(因为MoveNext返回一个bool)。
答案 0 :(得分:2)
我怀疑这是因为合同调用最终在生成的迭代器实现类型的MoveNext()
中。试试这个:
public static IEnumerable<UnboundTag> GetUnboundTagsRecursively
(Type bindingType)
{
Contract.Requires(bindingType != null);
Contract.Ensures(Contract.Result<IEnumerable<UnboundTag>>() != null);
return GetUnboundTagsRecursivelyImpl(bindingType);
}
private static IEnumerable<UnboundTag> GetUnboundTagsRecursivelyImpl
(Type bindingType)
{
// Iterator block code here
}
现在,您可能需要做一些额外的工作才能使这些方法在没有任何合同违规的情况下进行编译。例如:
public static IEnumerable<UnboundTag> GetUnboundTagsRecursively
(Type bindingType)
{
Contract.Requires(bindingType != null);
Contract.Ensures(Contract.Result<IEnumerable<UnboundTag>>() != null);
IEnumerable<UnboundTag> ret = GetUnboundTagsRecursivelyImpl(bindingType);
// We know it won't be null, but iterator blocks are a pain.
Contract.Assume(ret != null);
return ret;
}
这样效率稍低,因为它会执行两次无效检查。它也有效地通过Assume
来抑制警告。
听到代码合同团队正在努力解决这个问题我不会感到惊讶......我想我听说过类似的东西,但我记不起细节了。 2009年9月发布的release notes包括:
对迭代器合同的初步支持
...但假设您使用的是最新版本,可能是初始支持还不够:)