1

次の Linq to Objects クエリがあります。

var query = this._vaporizerData
    .Where(row => row.Coater == coater)
    .Where(row => row.Distributor == distributor)
    .Where(row => row.PowerTubeA.HasValue);

if (query.Any())
{
    tubeA = query
        .Where(row => row.CoaterTime.Value >= readTime.AddMinutes(-5))
        .Where(row => row.CoaterTime.Value <= readTime)
        .OrderByDescending(row => row.CoaterTime)
        .Select(row => row.PowerTubeA)
        .First()
        .Value;
}

query.Any() 行が実行されると、最初の Linq クエリが評価されることがわかっています。私の質問はこれです。最初のクエリの結果が 5 行のデータであるとします。2 番目のクエリ (「tubeA = query」で始まる) を実行すると、最初のクエリから返された 5 行だけに対してこれを評価するのは正しいですか?

どうもありがとう。

4

4 に答える 4

5

query.Any()行が実行されると、最初のLinqクエリが評価されることを知っています。

まあ、ある種。必要な限り評価されます。したがって、100万行ある_vaporizerDataが、最初の行がフィルターに一致する場合、残りのデータを反復処理することはありません。

2番目のクエリ('tubeA = query'で始まる)を実行すると、最初のクエリから返された5行だけに対してこれが評価されるのは正しいですか?

はい。ただし、最初に一致しなかったすべての行に対してフィルターをチェックして、それらの行を再度検索する必要があります。繰り返しになりますが、100万行ある_vaporizerDataが、今回は最後の行だけが元のクエリと一致する場合、999,999の一致しない行すべてを再度チェックすることになります。

また、呼び出しと呼び出しの_vaporizerData間の内容が他に変更された場合、実際には元のデータソースに戻るため、それらの変更が表示されます。Any()First()

クエリを1回だけ実行する方がよいでしょう。

var minTime = readTime.AddMinutes(-5); // Avoid recalculation
var result = this._vaporizerData
                 .Where(row => row.Coater == coater)
                 .Where(row => row.Distributor == distributor)
                 .Where(row => row.PowerTubeA.HasValue);
                 .Where(row => row.CoaterTime.Value >= minTime)
                 .Where(row => row.CoaterTime.Value <= readTime)
                 .OrderByDescending(row => row.CoaterTime)
                 .Select(row => row.PowerTubeA)
                 .FirstOrDefault();

if (result != null)
{
    tubeA = result.Value;
}

編集:コメントに記載されている2つの重要なポイント:

  • 最初のクエリがアイテムに一致したが、2番目のクエリが一致しなかった場合、元のクエリは例外をスローします。result上記のコードは、nullとして終わるだけです。それはあなたにとって問題かもしれないし、そうでないかもしれません。
  • MoreLINQを使用する場合は、andを使用する.MaxBy(row => row.CoaterTime)代わりにを使用することでこれをより効率的にすることができます。IIRC、ただし空の入力では失敗するため、上記のバージョンではなく、元のバージョンを使用している場合にのみこれを行う必要があります。OrderByDescendingFirstFirst
于 2012-08-30T17:19:04.083 に答える
1

コレクションがリストであると想像してみましょう。それらが最初の 3 つの基準に一致するかどうかが次のようになると想像してみましょう。

F, T, F, F, F, T, F, F, F, T, F, F, T, F, T, F, F, F

(T = 一致、F = 一致しない)

への呼び出し Anyは、2 つの要素を調べます。その時点で、少なくとも 1 つの項目があることがわかります。それ以上の要素は調べませんが、true を返します。

他の述語とも一致するものは次のとおりです。

F, F, F, F, F, F, F, F, F, F, F, F, T, F, T, F, F, F

OrderByDescending結果を生成するには、すべての項目を調べる必要があるためです。どちらが先かを知る方法は他にありません。

そうでない場合は、13 を調べて「最初」と見なし、完了します。

一方で、このような一般的なケースは、ToList()処理を高速化できるケースです。単にクエリを再利用するのではなく、クエリ結果を再利用するため、中間結果を保存する利点が、リストを作成するコストを上回る可能性があります。

一方、クエリの他の再利用では、まさにこの種の動作が必要です。

于 2012-08-30T17:25:27.080 に答える
0

2番目のクエリは、最初のクエリの結果を使用しません。したがって、2つのクエリでオブジェクトをヒットしています。の場合は1 .Any().First()

オブジェクトを2回ヒットせずに最初のクエリの結果を再利用する場合は、次のToList()ように使用します。

var query = this._vaporizerData
    .Where(row => row.Coater == coater)
    .Where(row => row.Distributor == distributor)
    .Where(row => row.PowerTubeA.HasValue).ToList();

これにより、最初のクエリ結果がに格納されますquery。それ以外のquery場合、値は保持されませんが、実際にはクエリを作成するためのステートメントです。

于 2012-08-30T17:18:36.083 に答える
0

基本コレクションに、クエリが返すよりも多くのオブジェクトがある場合は、おそらく を呼び出すべきではありません。おそらくAny、これらのオブジェクトの多くが最初の一連の基準に対して 2 回評価される可能性が高いからです。ToList代わりに、 orを呼び出して最初のクエリを 1 回実行しますToArray

var interimResults = this._vaporizerData 
    .Where(row => row.Coater == coater) 
    .Where(row => row.Distributor == distributor) 
    .Where(row => row.PowerTubeA.HasValue)
    .ToList(); 

if (interimResults.Count > 0) 
{ 
    tubeA = interimResults
        .Where(row => row.CoaterTime.Value >= readTime.AddMinutes(-5)) 
        .Where(row => row.CoaterTime.Value <= readTime) 
        .OrderByDescending(row => row.CoaterTime) 
        .Select(row => row.PowerTubeA) 
        .First() 
        .Value; 
} 
于 2012-08-30T17:24:48.193 に答える