27

「無限」の IEnumerable の簡単な例は次のようになります。

IEnumerable<int> Numbers() {
  int i=0;
  while(true) {
    yield return unchecked(i++);
  }
}

そんなこと知ってる

foreach(int i in Numbers().Take(10)) {
  Console.WriteLine(i);
}

var q = Numbers();
foreach(int i in q.Take(10)) {
  Console.WriteLine(i);
}

どちらも正常に動作します (そして 0 ~ 9 の数字を出力します)。

しかし、 のような式をコピーまたは処理する際に落とし穴はありますqか? それらが常に「怠け者」と評価されるという事実に頼ることができますか? 無限ループを引き起こす危険はありますか?

4

5 に答える 5

21

怠惰でバッファリングされていないメソッドのみを呼び出す限り、問題はありません。、Skip、などは問題ありませんTakeSelectただしMin、、、CountなどOrderByはおかしくなります。

うまくいくかもしれませんが、注意が必要です。または、安全対策として a を挿入しTake(somethingFinite)ます (または、データが多すぎると例外をスローする他のカスタム拡張メソッド)。

例えば:

public static IEnumerable<T> SanityCheck<T>(this IEnumerable<T> data, int max) {
    int i = 0;
    foreach(T item in data) {
        if(++i >= max) throw new InvalidOperationException();
        yield return item;
    }
}
于 2010-04-29T19:03:21.510 に答える
8

はい、上記のコードが遅延して実行されることが保証されています。(あなたのコードでは) 永遠にループするように見えますが、あなたのコードは実際には次のようなものを生成します:

IEnumerable<int> Numbers()
{
    return new PrivateNumbersEnumerable();
}

private class PrivateNumbersEnumerable : IEnumerable<int>
{
    public IEnumerator<int> GetEnumerator() 
    { 
        return new PrivateNumbersEnumerator(); 
    }
}

private class PrivateNumbersEnumerator : IEnumerator<int>
{
    private int i;

    public bool MoveNext() { i++; return true; }   

    public int Current
    {
        get { return i; }
    }
}

(これはコードにかなり固有であるため、明らかに正確に生成されるわけではありませんが、それでも同様であり、遅延評価される理由を示す必要があります)。

于 2010-04-29T19:02:07.437 に答える
5

最後まで読み込もうとする貪欲な関数は避ける必要があります。これには、 、/ 、および集計/ /などのEnumerable拡張機能が含まれます。CountToArrayToListAvgMinMax

無限の遅延リストに問題はありませんが、それらの処理方法について意識的に決定する必要があります。

Takeすべてを必要としない場合でも、上限を設定して無限ループの影響を制限するために使用します。

于 2010-04-29T19:03:22.507 に答える
2

はい、コードは常に無限ループなしで機能します。後で誰かがやって来て、物事を台無しにするかもしれません。彼らがやりたいとしましょう:

var q = Numbers().ToList();

次に、あなたはホースです !のように、多くの「集約」関数はあなたを殺しますMax()

于 2010-04-29T19:04:44.060 に答える
0

遅延評価ではない場合、最初の例はそもそも期待どおりに機能しません。

于 2010-04-29T19:04:47.890 に答える