带代码契约的迭代器中的错误?

时间:2009-07-02 02:43:22

标签: c# .net .net-4.0 code-contracts microsoft-contracts

以下代码在前置条件下失败。这是代码合同中的错误吗?

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;
    }
}

5 个答案:

答案 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的开始时检查,而不是在每次迭代期间检查。