4

IEnumerator.Currentのドキュメントを検討してください。

また、MoveNext への最後の呼び出しがコレクションの終了を示す false を返した場合、Current は例外をスローします。

ただし、これは反復子ブロックでは発生しません。例えば:

void Main()
{
    using (var enumerator = GetCounter().GetEnumerator())
    {
        for (int i = 0; i < 10; i++)
        {
            enumerator.MoveNext();
            Console.WriteLine (enumerator.Current);
        }
    }
}

static IEnumerable<int> GetCounter()
{
   for (int count = 0; count < 3; count++)
   {
       yield return count;
   }
}

単純に 8 回出力2され、例外はスローされません。コンパイラの変換を見ると、はCurrent、常にフィールドの値を返すだけで、それ以上のものは何も返さない単純なフィールド バックアップ プロパティです。おそらく、それは何らかの形の最適化ですか?それでも、これは契約違反のように見えます。

4

1 に答える 1

4

IEnumerator.Currentのドキュメントについては正しいのですが、IEnumerator<T>.Currentのドキュメントでは、このようなシナリオではプロパティが未定義であると述べています。イテレータを使用すると、「2」が返されます。List<T>の列挙子は を返しdefault(T)T[]は例外をスローします。これらは未定義であるため、すべて有効な実装です。

次の条件のいずれかでは、電流は定義されません。

  • 列挙子は、列挙子が作成された直後、コレクション内の最初の要素の前に配置されます。Current の値を読み取る前に、MoveNext を呼び出して列挙子をコレクションの最初の要素に進める必要があります。
  • MoveNext の最後の呼び出しでfalseが返されました。これは、コレクションの終了を示します。
  • 要素の追加、変更、または削除など、コレクションで行われた変更により、列挙子が無効になります。

インターフェイスを実装していても、 から生成されたコードはyield returnを正しく実装していないことに注意してください。この場合、IEnumerator引き続き返されるためです。2

IEnumerator enumerator = GetCounter().GetEnumerator();
for (int i = 0; i < 10; i++)
{
    enumerator.MoveNext();
    Console.WriteLine (enumerator.Current);
}

(比較のために、List<T>それは正しいですか:IEnumerator.Current終了後に取得すると例外がスローさIEnumerator<T>.Currentれ、終了後に呼び出すとが返されますdefault(T)

于 2013-10-28T19:57:17.680 に答える