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

私の推測では、これは反復子の遅延の性質に関係しています。コントラクト処理は、C# コードではなく、最終的に発行された IL で行われることに注意してください。これは、反復子やラムダ式などの機能の生成コードを考慮する必要があることを意味します。

そのコードを逆コンパイルすると、「i」が実際にはパラメーターではないことがわかります。イテレータを実装するために使用されるクラスの変数になります。したがって、コードは実際には次のようになります

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

私はコントラクト API にあまり詳しくありませんが、生成されたコードを検証するのははるかに難しいと思います。

于 2009-07-02T02:46:35.800 に答える
0

このコードは、インターレーターのコードコントラクトがサポートされている最終バージョンの.NET 4.0(試してみたばかり)で動作しますが、最近わか​​ったように、常に正しく動作するとは限りません(詳細はこちらをご覧ください)。

于 2010-06-27T08:56:02.363 に答える
0

これは、過去の CodeContract リライタの問題であった可能性があります。しかし、現在のバージョンはあなたの例ではうまくいくようです。イテレータ/遅延評価などの問題はありません。パラメータ i は値によってキャプチャされ、反復中に変更されません。コントラクトは、各反復中ではなく、Test の呼び出しの開始時にのみこれをチェックする必要があります。

于 2013-04-24T22:04:58.873 に答える
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 に答える