Proper Linq where clauses

133

J'écris pas mal de linq dans ma vie de tous les jours, mais surtout des déclarations simples. J'ai remarqué que lors de l'utilisation de clauses where, il existe de nombreuses façons de les écrire et chacune a les mêmes résultats pour autant que je sache. Par exemple;

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

Cela semble équivalent au moins en ce qui concerne les résultats:

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

Alors, y a-t-il vraiment une différence autre que la syntaxe? Si oui, quel est le style préféré et pourquoi?

AR
la source
203
Vous avez une Fatpropriété booléenne ? C'est tout simplement méchant.
Bala R
104
@Bala R: Hé, si votre chien est gros, votre chien est gros.
AR

Réponses:

76

Le second serait plus efficace car il n'a qu'un seul prédicat à évaluer par rapport à chaque élément de la collection où, comme dans le premier, il applique le premier prédicat à tous les éléments en premier et le résultat (qui est réduit à ce stade) est utilisé pour le deuxième prédicat et ainsi de suite. Les résultats sont réduits à chaque passage, mais cela implique toujours plusieurs passes.

De plus, le chaînage (première méthode) ne fonctionnera que si vous effectuez un ET vos prédicats. Quelque chose comme ça x.Age == 10 || x.Fat == truene fonctionnera pas avec votre première méthode.

Bala R
la source
1
Les conditions OR en chaîne sont quelque peu possibles en utilisant cette extension: albahari.com/nutshell/predicatebuilder.aspx
jahu
142

EDIT: LINQ to Objects ne se comporte pas comme je m'y attendais. Vous serez peut-être intéressé par le billet de blog que je viens d'écrire à ce sujet ...


Ils sont différents en termes de ce que l'on appellera - le premier équivaut à:

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

où ce dernier équivaut à:

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

Maintenant , quelle différence cela fait en fait dépend de la mise en œuvre d' Whereêtre appelé. S'il s'agit d'un fournisseur basé sur SQL, je m'attendrais à ce que les deux finissent par créer le même SQL. Si c'est dans LINQ to Objects, le second aura moins de niveaux d'indirection (il n'y aura que deux itérateurs impliqués au lieu de quatre). La question de savoir si ces niveaux d'indirection sont significatifs en termes de vitesse est une autre question.

En règle générale, j'utiliserais plusieurs whereclauses si elles avaient l'impression de représenter des conditions significativement différentes (par exemple, l'une est whereliée à une partie d'un objet et l'autre est complètement séparée) et une clause lorsque diverses conditions sont étroitement liées (par exemple, une valeur particulière est supérieur à un minimum et inférieur à un maximum). En gros, il vaut la peine de considérer la lisibilité avant toute légère différence de performance.

Jon Skeet
la source
1
@JonSkeet Peut-être que je me trompe, mais après un rapide examen de l'implémentation de Linq Where, je n'en suis pas sûr. Nested Where sont combinés par une méthode statique 'CombinePredicates'. La collection est itérée une seule fois par un seul itérateur avec le prédicat combiné. Bien sûr, la combinaison de func a un impact sur les performances, mais il est très limité. Ça va ?
Cybermaxs
@Cybermaxs: Vous ne savez pas exactement quoi ? Je n'ai jamais suggéré que la collection serait répétée plus d'une fois.
Jon Skeet
@JonSkeet oui bien sûr mais à la fin tous les prédicats sont combinés et un seul itérateur est impliqué. RegardezEnumerable.WhereSelectEnumerableIterator.
Cybermaxs
La page à laquelle vous avez lié est maintenant fermée. Pourriez-vous mettre à jour le lien si l'article est toujours en ligne ailleurs? Merci.
Asad Saeeduddin
2
@Asad: mis à jour. (Mon blog a déménagé.)
Jon Skeet
13

Le premier sera mis en œuvre:

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

Par opposition au beaucoup plus simple (et beaucoup plus rapide probablement plus rapide):

// all in one fell swoop
Collection.Where(x => x.Age == 10 && x.Name == "Fido" && x.Fat == true)
user7116
la source
6
"Beaucoup plus rapide"? Nous ne savons même pas encore quelle implémentation LINQ est impliquée, il est donc difficile d'y attacher une implication de performance.
Jon Skeet
Dans le cas général, ce dernier ne nécessite qu'une boucle. Un fournisseur peut choisir d'aplatir le premier exemple, mais ce n'est pas obligatoire.
user7116
2
En effet ... mais vous prétendez que ce dernier est beaucoup plus rapide. Il n'est pas du tout clair que ce sera beaucoup plus rapide du tout - après tout, l'importance de la différence de performance dépendra de la façon dont cela est utilisé.
Jon Skeet
1
@Jon: pas de désaccord. Comme vous le remarquez, la réalité pourrait être que le fournisseur LINQ effectue des transformations d'optimisation utiles dans l'expression. Mais étant donné que le second ne nécessite qu'une seule boucle et bénéficie d'un court-circuit booléen, il est difficile de voir pourquoi il ne devrait pas être étiqueté comme "beaucoup plus rapide" en termes généraux. Si l'OP ne comporte que 5 éléments, mon argument est sans objet.
user7116
11

quand je cours

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

et

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

contre ma table client, il génère la même requête 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)

donc dans la traduction en sql il n'y a pas de différence et vous avez déjà vu dans d'autres réponses comment elles seront converties en expressions lambda

Muhammad Adeel Zahid
la source
ok, alors vous voulez dire que cela n'aura aucun effet sur les performances si j'utilise l'un de ceux-ci?
Bimal Das
Les clauses WHERE sont enchaînées en fait. Donc, peu importe comment vous l'écrivez. Il n'y a aucune différence de performance.
hastrb
3

En regardant sous le capot, les deux instructions seront transformées en différentes représentations de requête. Selon le QueryProviderde Collection, cela peut être optimisé ou non.

Lorsqu'il s'agit d'un appel linq-to-object, plusieurs clauses where mèneront à une chaîne de IEnumerables qui se lisent les uns les autres. L'utilisation du formulaire à clause unique améliorera les performances ici.

Lorsque le fournisseur sous-jacent le traduit en une instruction SQL, les chances sont bonnes que les deux variantes créent la même instruction.

David Schmitt
la source