適切なLinq where句


133

私は日常生活でかなりの量のlinqを書いていますが、ほとんどが単純なステートメントです。where句を使用する場合、それらを書く方法はたくさんあり、私が知る限り、それぞれ同じ結果になることに気づきました。例えば;

from x in Collection
  where x.Age == 10
  where x.Name == "Fido"
  where x.Fat == true
  select x;

少なくとも結果に関する限り、これと同等であると思われます。

from x in Collection
  where x.Age == 10 &&
        x.Name == "Fido" &&
        x.Fat == true
  select x;

では、構文以外に本当に違いがあるのでしょうか?もしそうなら、好ましいスタイルは何ですか?なぜですか?


203
ブールFatプロパティがありますか?それは明白な意味です。
Bala R

104
@Bala R:ねえ、あなたの犬が太っているなら、あなたの犬は太っている。
AR

回答:


76

2つ目は、コレクション内の各アイテムに対して評価する述語が1つしかないため、最初のすべてのアイテムに最初の述語を最初に適用し、結果(この時点で絞り込まれます)があるため、より効率的です。 2番目の述語などに使用されます。結果はパスごとに絞り込まれますが、それでも複数のパスが含まれます。

また、連鎖(最初の方法)は、述語のAND演算を行う場合にのみ機能します。このようなものx.Age == 10 || x.Fat == trueは、最初の方法では機能しません。


1
この拡張を使用すると、チェーンOR条件がいくらか可能になります。albahari.com
nutshell

142

編集:LINQ to Objectsは、期待したとおりに動作しません。私がこれについて書いたばかりのブログ記事に興味があるかもしれません...


それらは何と呼ばれるかという点で異なります-最初のものは以下と同等です:

Collection.Where(x => x.Age == 10)
          .Where(x => x.Name == "Fido")
          .Where(x => x.Fat == true)

後者は次と同等です:

Collection.Where(x => x.Age == 10 && 
                      x.Name == "Fido" &&
                      x.Fat == true)

ここで実際にどのような違いが生じるかは、Where呼び出される実装によって異なります。SQLベースのプロバイダーであれば、2つが同じSQLを作成することになると思います。LINQ to Objectsにある場合、2番目の間接指定のレベルは少なくなります(4つではなく2つの反復子が関係することになります)。これらの間接レベルが速度の点で重要かどうかは、別の問題です。

通常、where条件が大きく異なる場合(たとえば、1つはオブジェクトの一部を処理し、もう1つは完全に分離している場合)にいくつかの句を使用しwhere、さまざまな条件が密接に関連している場合(たとえば特定の値)に1つの句を使用します。最小値より大きく、最大値より小さい)。基本的に、パフォーマンスにわずかな違いが出る前に、読みやすさを検討する価値があります。


1
@JonSkeet多分私は間違っているかもしれませんが、Linq Where実装の簡単なレビューの後、私はそれがわかりません。ネストされたWhereは、静的メソッド 'CombinePredicates'によって結合されます。コレクションは、結合された述語を持つ単一のイテレータによって1回だけ繰り返されます。もちろん、funcを組み合わせるとパフォーマンスに影響がありますが、非常に制限されています。大丈夫ですか ?
Cyber​​maxs 2013

@Cyber​​maxs:正確には何がわかりませんか?コレクションを2回以上繰り返すことを提案したことはありません。
Jon Skeet 2013

@JonSkeetはい、もちろんですが、最後にすべての述語が結合され、1つの反復子だけが組み込まれます。Enumerable.WhereSelectEnumerableIteratorを見てください。
Cyber​​maxs 2013

リンク先のページはダウンしています。記事がまだ他の場所にある場合は、リンクを更新していただけませんか?ありがとう。
Asad Saeeduddin 2015

2
@Asad:更新されました。(私のブログは移動しました。)
Jon Skeet、2015

13

最初のものが実装されます:

Collection.Where(x => x.Age == 10)
          .Where(x => x.Name == "Fido") // applied to the result of the previous
          .Where(x => x.Fat == true)    // applied to the result of the previous

単純多くの(とは対照的に、はるかに速く、おそらく速いです):

// all in one fell swoop
Collection.Where(x => x.Age == 10 && x.Name == "Fido" && x.Fat == true)

6
「はるかに速い」?どのLINQ実装が関係しているかさえまだわかっていないため、パフォーマンスに影響を与えることは困難です。
Jon Skeet、2011年

一般的なケースでは、後者は1つのループのみを必要とします。プロバイダーは最初の例をフラット化することを選択できますが、必須ではありません。
user7116

2
確かに...しかし、あなたは後者はるかに速いと主張しています。それが非常に高速になることはまったく明らかではありません。結局のところ、パフォーマンスの違いの重要性は、これがどのように使用されているかによって異なります。
Jon Skeet

1
@ジョン:意見の相違はありません。お気づきのように、LINQプロバイダーは、式に対して有用な最適化変換を実行します。しかし、2番目のループは1つのループしか必要とせず、ブール短絡のメリットがあるため、一般的な用語で「はるかに高速」とラベル付けされるべきではない理由を理解するのは困難です。OPに5つの要素しかない場合、私のポイントは意味がありません。
user7116

11

私が走るとき

from c in Customers
where c.CustomerID == 1
where c.CustomerID == 2
where c.CustomerID == 3
select c

そして

from c in Customers
where c.CustomerID == 1 &&
c.CustomerID == 2 &&
c.CustomerID == 3
select c customer table in linqpad

私の顧客テーブルに対して、同じSQLクエリを出力しました

-- Region Parameters
DECLARE @p0 Int = 1
DECLARE @p1 Int = 2
DECLARE @p2 Int = 3
-- EndRegion
SELECT [t0].[CustomerID], [t0].[CustomerName]
FROM [Customers] AS [t0]
WHERE ([t0].[CustomerID] = @p0) AND ([t0].[CustomerID] = @p1) AND ([t0].[CustomerID] = @p2)

SQLに翻訳しても違いはなく、ラムダ式に変換される方法は他の回答ですでに見ています


わかりました。次に、これらのいずれかを使用してもパフォーマンスに影響がないと言いたいのですか?
Bimal Das

WHERE句は実際には連鎖しています。したがって、どのように記述するかは問題ではありません。パフォーマンスに違いはありません。
hastrb 2017

3

内部で見ると、2つのステートメントは異なるクエリ表現に変換されます。応じてQueryProviderCollection、これは離れて最適化されたかではない可能性があります。

これがlinq-to-objectの呼び出しである場合、複数のwhere句は、相互に読み取るIEnumerableのチェーンにつながります。ここでは、単一句フォームを使用するとパフォーマンスが向上します。

基になるプロバイダーがそれをSQLステートメントに変換する場合、両方のバリアントが同じステートメントを作成する可能性が高くなります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.