以下代码在前置条件下失败。这是代码合同中的错误吗?
static class Program
{
static void Main()
{
foreach (var s in Test(3))
{
Console.WriteLine(s);
}
}
static IEnumerable<int>Test (int i)
{
Contract.Requires(i > 0);
for (int j = 0; j < i; j++)
yield return j;
}
}
答案 0 :(得分:2)
我的猜测是这与迭代器的延迟性质有关。请记住,合同处理将在最终发出的IL上发生,而不是C#代码。这意味着您必须考虑生成的代码,例如迭代器和lambda表达式。
如果您反编译该代码,您会发现“i”实际上不是参数。它将是类中用于实现迭代器的变量。所以代码看起来更像是下面的
class IteratorImpl {
private int i;
public bool MoveNext() {
Contract.Require(i >0);
..
}
}
我对合同API并不十分熟悉,但我的猜测是生成的代码更难以验证。
答案 1 :(得分:0)
请记住,迭代器在枚举之前不会运行,并且会在后端编译成一些特殊的酱。如果你想验证参数,你应该遵循的一般模式,这可能适用于契约,是有一个包装函数:
static IEnumerable<int> Test (int i)
{
Contract.Requires(i > 0);
return _Test(i);
}
private static IEnumerable<int> _Test (int i)
{
for (int j = 0; j < i; j++)
yield return j;
}
这样,Test()会在调用参数时检查参数,然后返回_Test(),实际上只返回一个新类。
答案 2 :(得分:0)
这是一个与blog post相关的关于单元测试,迭代器,延迟执行和你的主题。
延迟执行是这里的问题。
答案 3 :(得分:0)
此代码适用于最终版本的.NET 4.0(刚刚尝试过),其中支持交互中的代码约定,但正如我最近发现的那样,它并不总是正常工作(阅读更多{ {3}})。
答案 4 :(得分:0)
这可能是CodeContract重写器过去的一个问题。但目前的版本似乎在你的例子上做得很好。迭代器/延迟评估等没有问题。参数i由值捕获,在迭代期间不会改变。合同应仅在调用Test的开始时检查,而不是在每次迭代期间检查。