1

次のような節をチェーンすると

var results = elements
    .Where(n => n > 3)
    .Where(n => n % 2 == 0);

これはただより遅いですか

var results = elements.Where(n => n > 3 && n % 2 == 0);

理由を説明してください。

編集: POCO オブジェクトでさえ 2 回反復するというコンセンサスがあるようです。この場合、マイクロソフトがこれらの述語を組み合わせない理由を誰かが説明できます。Enumerable.CombinePredicates私はこれをやったと思ったことに出くわしました。誰かがこれが何をするのか説明してください。

4

2 に答える 2

2

LINQ-to-objects について話している場合、それぞれWhereに新しい反復子ステート マシンを設定する必要がありますが、これには費用がかかります。そのため、両方の条件を組み合わせるよりも遅くなります。

LINQ-to-何か他のものについて話している場合は、状況によって異なります。エクストラWhereは、どこかで余分な文字列連結を伴うだけかもしれません。それでもより高価になる可能性がありますが、正確な違いは LINQ プロバイダーによって異なります。

于 2012-12-05T14:03:28.980 に答える
2

編集:少し近づいて見ました。拡張メソッドWhereEnumerableIteratorによって返される は、実際にはメソッドをオーバーライドし、述語を 1 つのコールバックに結合します。WhereWhere

public override IEnumerable<TSource> Where(Func<TSource, bool> predicate) {
    return new Enumerable.WhereEnumerableIterator<TSource>(
        this.source, 
        Enumerable.CombinePredicates<TSource>(this.predicate, predicate));
}

private static Func<TSource, bool> CombinePredicates<TSource>(
    Func<TSource, bool> predicate1, Func<TSource, bool> predicate2
    ) {
    return (TSource x) => predicate1(x) && predicate2(x);
}

したがって、私のマシンで見た速度の違いは、おそらく別の原因によるものです。

elements最初の例では、コレクションを 1 回 ループして条件を満たすアイテムitem > 3を検索し、もう一度条件を満たしたアイテムを検索しますitem % 2 == 0

2 番目の例では、コレクションを 1 回ループしてelements、条件を満たす項目を見つけますitem > 3 && item % 2 == 0

提供されている例では、2 番目のループはelements1 回だけループするため、常に最初よりも高速です。

これは、私のマシン (.NET 3.5) で取得したかなり一貫した結果の例です。

    var stopwatch = new System.Diagnostics.Stopwatch();
    var elements = Enumerable.Range(1, 100000000);
    var results = default(List<int>);
    stopwatch.Start();
    results = elements.Where(n => n > 3).Where(n => n % 2 == 0).ToList();
    stopwatch.Stop();
    Console.WriteLine(stopwatch.Elapsed);
    stopwatch.Reset();
    stopwatch.Start();
    results = elements.Where(n => n > 3 && n % 2 == 0).ToList();
    stopwatch.Stop();
    Console.WriteLine(stopwatch.Elapsed);
    Console.WriteLine("Done");
    Console.ReadLine();

結果:

00:00:03.0932811
00:00:02.3854886
Done

編集:
@Rawling は、私の説明が POCO オブジェクトのコレクションで使用される LINQ にのみ適用されるという点で正しいです。LINQ-to-SQL、NHibernate、EF などへのインターフェイスとして使用すると、結果はより実装に依存します。

于 2012-12-05T14:18:02.837 に答える