7

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

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

5 回答 5

2

我的猜测是这与迭代器的延迟特性有关。请记住,合约处理将发生在最终发出的 IL 上,而不是 C# 代码上。这意味着您必须考虑为迭代器和 lambda 表达式等功能生成的代码。

如果您反编译该代码,您会发现“i”实际上不是一个参数。它将是用于实现迭代器的类中的一个变量。所以代码实际上看起来更像下面

class IteratorImpl {
  private int i;
  public bool MoveNext() {
    Contract.Require(i >0);
    ..
  }
}

我对合约 API 不是很熟悉,但我猜生成的代码更难验证。

于 2009-07-02T02:46:35.800 回答
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(),它实际上只是返回一个新类。

于 2009-07-02T02:49:56.963 回答
0

这是一篇与这个主题相关的博客文章,涉及单元测试、迭代器、延迟执行和您。

延迟执行是这里的问题。

于 2009-07-02T02:50:49.833 回答
0

此代码将适用于支持交互器中的代码协定的 .NET 4.0 最终版本(刚刚尝试过),但正如我最近发现的那样,它并不总是能正常工作(在此处阅读更多内容)。

于 2010-06-27T08:56:02.363 回答
0

这可能是过去 CodeContract 重写器中的一个问题。但是当前版本在您的示例中似乎做得很好。迭代器/延迟评估等在这里没有问题。参数 i 由值捕获并且在迭代期间不会改变。合约应仅在调用 Test 开始时检查这一点,而不是在每次迭代期间。

于 2013-04-24T22:04:58.873 回答