¿Por qué IEnumerable no es devuelto por el método LINQ Where() totalmente consumido por Count()?
Frecuentes
Visto 178 equipos
1
Yo era consciente de que el Count()
El método proporcionado por LINQ tenía una optimización mediante la cual verificaría si la secuencia fuente implementada ICollection<T>
y si es así llamar al Count
propiedad en lugar de iterar sobre toda la colección. Cuando se utiliza esta optimización, el subyacente IEnumerable<T>
no se consume, por lo que puede ser consumido por otras llamadas posteriores Count()
.
En particular, la sobrecarga de Count que acepta un predicado no realiza dicha optimización porque debe inspeccionar el valor de cada elemento.
Ahora considere el siguiente programa completo:
using System;
using System.Collections.Generic;
using System.Linq;
namespace count_where_issue
{
class Program
{
static void Main(string[] args)
{
IEnumerable<int> items = new List<int> {1, 2, 3, 4, 5, 6};
IEnumerable<int> evens = items.Where(y => y % 2 == 0);
int count = evens.Count();
int first = evens.First();
Console.WriteLine("count = {0}", count);
Console.WriteLine("first = {0}", first);
}
}
}
que imprime,
count = 3
first = 2
Mi expectativa era que el Conde necesitaría consumir todo el evens
secuencia devuelta por Where()
y la llamada posterior a evens.First()
fallaría con InvalidOperationException
porque la secuencia no contendría elementos.
¿Por qué este programa funciona de la manera que lo hace? Normalmente no intentaría usar un IEnumerable<T>
siguiendo una llamada a `Count(). ¿Sería imprudente confiar en este comportamiento?
3 Respuestas
2
No sé si esto es lo que quieres decir, pero evens.Count()
y evens.First()
ambos enumeran el items.Where(...)
.
Puede ver esto en el resultado de lo siguiente:
class Program
{
static void Main(string[] args)
{
IEnumerable<int> items = new List<int> { 1, 2, 3, 4, 5, 6 };
IEnumerable<int> evens = items.Where(y => isEven(y));
int count = evens.Count();
Console.WriteLine("count = {0}", count);
int first = evens.First();
Console.WriteLine("first = {0}", first);
}
private static bool isEven(int y)
{
Console.Write(y + ": ");
bool result = y % 2 == 0;
Console.WriteLine(result);
return result;
}
}
Qué es:
1: False
2: True
3: False
4: True
5: False
6: True
count = 3
1: False
2: True
first = 2
Respondido el 30 de junio de 12 a las 19:06
1
La altura de la cúpula es XNUMX metros, que es IEnumerator<T>
no serías capaz de usar. Sin embargo, Count y First crean cada uno un enumerador separado (a través de llamadas a GetEnumerator
en IEnumerable<T>
).
Respondido el 30 de junio de 12 a las 19:06
1
¿Qué le dio la impresión de que Count() afecta el IEnumerable original de alguna manera?
Los documentos no mencionan nada por el estilo...
Respondido el 30 de junio de 12 a las 19:06
Tuve la impresión de leer las publicaciones del blog de Jon Skeet sobre el funcionamiento de LINQ, en particular este artículo: msmvps.com/blogs/jon_skeet/archive/2010/12/26/… - Rob Smallshire
No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas c# linq linq-to-objects or haz tu propia pregunta.
Presumiblemente, esto solo es efectivo porque la última fuente de datos, la Lista, se puede repetir más de una vez. - Rob Smallshire