Si escribo algo como esto:
var things = mythings
.Where(x => x.IsSomeValue)
.Where(y => y.IsSomeOtherValue)
¿Es esto lo mismo que:
var results1 = new List<Thing>();
foreach(var t in mythings)
if(t.IsSomeValue)
results1.Add(t);
var results2 = new List<Thing>();
foreach(var t in results1)
if(t.IsSomeOtherValue)
results2.Add(t);
¿O hay algo de magia debajo de las cubiertas que funciona más así:
var results = new List<Thing>();
foreach(var t in mythings)
if(t.IsSomeValue && t.IsSomeOtherValue)
results.Add(t);
¿O es algo completamente diferente?

Respuestas:
Las consultas de LINQ son perezosas . Eso significa el código:
hace muy poco El enumerable original (
mythings) solo se enumera cuandothingsse consume el enumerable resultante ( ), por ejemplo, mediante unforeachbucle.ToList(), o.ToArray().Si llama
things.ToList(), es más o menos equivalente a su último código, con quizás una sobrecarga (generalmente insignificante) de los enumeradores.Del mismo modo, si usa un bucle foreach:
Es similar en rendimiento a:
Algunas de las ventajas de rendimiento del enfoque de la pereza para los enumerables (en lugar de calcular todos los resultados y almacenarlos en una lista) es que utiliza muy poca memoria (ya que solo se almacena un resultado a la vez) y que no hay un aumento significativo -costo por adelantado.
Si lo enumerable solo se enumera parcialmente, esto es especialmente importante. Considera este código:
La forma en que se implementa LINQ
mythingssolo se enumerará hasta el primer elemento que coincida con sus condiciones where. Si ese elemento está al principio de la lista, esto puede ser un gran aumento de rendimiento (por ejemplo, O (1) en lugar de O (n)).fuente
foreaches que LINQ usa invocaciones de delegado, que tienen algo de sobrecarga. Esto puede ser significativo cuando las condiciones se ejecutan muy rápidamente (lo que a menudo hacen).ToListoToArray. Si tal cosa se hubiera incorporado correctamenteIEnumerable, habría sido posible pedir una lista para "capturar" cualquier aspecto que pudiera cambiar en el futuro sin tener que generar todo.El siguiente código:
Es equivalente a nada, debido a la evaluación perezosa, no pasará nada.
Es diferente, porque la evaluación será lanzada.
Cada artículo de
mythingsserá entregado al primeroWhere. Si pasa, se le dará al segundoWhere. Si pasa, será parte de la salida.Entonces esto se parece más a esto:
fuente
Dejando de lado la ejecución diferida (que las otras respuestas ya explican, solo señalaré otro detalle), es más como en su segundo ejemplo.
Imaginemos que llame
ToListathings.La implementación de
Enumerable.Wheredevoluciones aEnumerable.WhereListIterator. Cuando invocasWhereesoWhereListIterator(también conocido como encadenamiento deWherellamadas), ya no invocasEnumerable.Where, sinoEnumerable.WhereListIterator.Whereque, en realidad, combina los predicados (usandoEnumerable.CombinePredicates).Entonces es más como
if(t.IsSomeValue && t.IsSomeOtherValue).fuente
No, no es lo mismo. En su ejemplo
thingses unIEnumerable, que en este punto todavía es solo un iterador, no una matriz o lista real. Además, dadothingsque no se usa, el ciclo nunca se evalúa. El tipoIEnumerablepermite iterar a través de elementosyield-ed por instrucciones de Linq y procesarlos aún más con más instrucciones, lo que significa que al final solo tiene un bucle.Pero tan pronto como agregue una instrucción como
.ToArray()o.ToList(), está ordenando la creación de una estructura de datos real, poniendo límites a su cadena.Consulte esta pregunta SO relacionada: /programming/2789389/how-do-i-implement-ienumerable
fuente