Linq adecuado donde cláusulas

133

Escribo una buena cantidad de linq en mi día a día, pero sobre todo declaraciones simples. Me he dado cuenta de que cuando utilizo las cláusulas where, hay muchas maneras de escribirlas y cada una tiene los mismos resultados por lo que puedo decir. Por ejemplo;

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

Parece ser equivalente a esto al menos en lo que respecta a los resultados:

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

Entonces, ¿hay realmente una diferencia que no sea la sintaxis? Si es así, ¿cuál es el estilo preferido y por qué?

Arkansas
fuente
203
Tienes una Fatpropiedad booleana ? Eso es muy malo.
Bala R
104
@Bala R: Hola, si tu perro está gordo, tu perro está gordo.
AR

Respuestas:

76

El segundo sería más eficiente, ya que solo tiene un predicado para evaluar contra cada elemento de la colección donde, como en el primero, está aplicando el primer predicado a todos los elementos primero y el resultado (que se reduce en este punto) es usado para el segundo predicado y así sucesivamente. Los resultados se reducen en cada pasada, pero aún implica varias pasadas.

Además, el encadenamiento (primer método) funcionará solo si está ANDinging sus predicados. Algo como esto x.Age == 10 || x.Fat == trueno funcionará con su primer método.

Bala R
fuente
1
Las condiciones de ORing de cadena son algo posibles usando esta extensión: albahari.com/nutshell/predicatebuilder.aspx
jahu
142

EDITAR: LINQ to Objects no se comporta como esperaba. Puede que te interese la publicación de blog que acabo de escribir sobre esto ...


Son diferentes en términos de lo que se llamará: el primero es equivalente a:

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

donde este último es equivalente a:

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

Ahora, la diferencia que realmente hace depende de la implementación de Whereser llamado. Si se trata de un proveedor basado en SQL, esperaría que ambos terminen creando el mismo SQL. Si está en LINQ to Objects, el segundo tendrá menos niveles de indirección (solo habrá dos iteradores involucrados en lugar de cuatro). Si esos niveles de indirección son significativos en términos de velocidad es una cuestión diferente.

Por lo general, usaría varias wherecláusulas si consideran que representan condiciones significativamente diferentes (por ejemplo, una tiene que ver con una parte de un objeto y otra está completamente separada) y una wherecláusula cuando varias condiciones están estrechamente relacionadas (por ejemplo, un valor particular es mayor que un mínimo y menor que un máximo). Básicamente vale la pena considerar la legibilidad antes de cualquier ligera diferencia de rendimiento.

Jon Skeet
fuente
1
@ JonSkeet Tal vez me equivoque, pero después de una rápida revisión de la implementación de Linq Where, no estoy seguro de eso. Los sitios anidados se combinan mediante un método estático 'CombinePredicates'. La colección se repite solo una vez por un solo iterador con el predicado combinado. Por supuesto, hay un impacto en el rendimiento de combinar func, pero es muy limitado. Estas bien ?
Cybermaxs
@Cybermaxs: ¿No estás seguro de qué , precisamente? Nunca sugerí que la colección se repitiera más de una vez.
Jon Skeet el
@ JonSkeet sí, por supuesto, pero al final todos los predicados se combinan y solo se invoca un iterador. MireEnumerable.WhereSelectEnumerableIterator.
Cybermaxs
La página a la que se vinculó está abajo ahora. ¿Podría actualizar el enlace si el artículo todavía está en otro lugar? Gracias.
Asad Saeeduddin
2
@Asad: actualizado. (Mi blog se ha movido.)
Jon Skeet
13

El primero se implementará:

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

A diferencia de lo mucho más simple (y mucho más rápido, presumiblemente más rápido):

// all in one fell swoop
Collection.Where(x => x.Age == 10 && x.Name == "Fido" && x.Fat == true)
usuario7116
fuente
66
¿"Mucho más rápido"? Aún no sabemos qué implementación de LINQ está involucrada, por lo que es difícil atribuirle alguna implicación de rendimiento.
Jon Skeet
En el caso general, este último solo requiere 1 bucle. Un proveedor podría elegir aplanar el primer ejemplo, pero no es obligatorio.
user7116
2
De hecho ... pero estás afirmando que este último es mucho más rápido. No está del todo claro que será significativamente más rápido en absoluto; después de todo, la importancia de la diferencia de rendimiento dependerá de cómo se esté utilizando.
Jon Skeet
1
@ Jon: no hay desacuerdo. Como notará, la realidad podría ser que el proveedor LINQ vaya y realice transformaciones útiles de optimización en la expresión. Pero dado que el segundo requiere solo un bucle y se beneficia del cortocircuito booleano, es difícil ver por qué no debería etiquetarse como "mucho más rápido" en términos generales. Si el OP solo tiene 5 elementos, mi punto es discutible.
user7116
11

cuando corro

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

y

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

contra mi tabla de Clientes, genera la misma consulta 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)

así que en la traducción a sql no hay diferencia y ya has visto en otras respuestas cómo se convertirán a expresiones lambda

Muhammad Adeel Zahid
fuente
ok, entonces ¿quieres decir que no tendrá ningún efecto en el rendimiento si uso alguno de estos?
Bimal Das
DONDE las cláusulas están encadenadas de hecho. Entonces, no importa cómo lo escribas. No hay diferencia de rendimiento.
hastrb
3

Mirando debajo del capó, las dos declaraciones se transformarán en diferentes representaciones de consulta. Dependiendo QueryProviderde Collectionesto, esto podría optimizarse o no.

Cuando se trata de una llamada de linq a objeto, las cláusulas where múltiples conducirán a una cadena de IEnumerables que se leerán entre sí. Usar el formulario de una sola cláusula ayudará al rendimiento aquí.

Cuando el proveedor subyacente lo traduce a una declaración SQL, hay muchas posibilidades de que ambas variantes creen la misma declaración.

David Schmitt
fuente