Diferencia entre el recuento de IEnumerable () y la longitud

¿Cuáles son las diferencias clave entre IEnumerable Count() y Length ?

Al invocar Count on IEnumerable supongo que se refiere al método de extensión Count on System.Linq.Enumerable . Length no es un método en IEnumerable sino más bien una propiedad en los tipos de matriz en .Net como int[] .

La diferencia es el rendimiento. Se garantiza que la propiedad Length es una operación O (1). La complejidad del método de extensión Count depende del tipo de tiempo de ejecución del objeto. Intentará convertir a varios tipos que admiten O (1) búsqueda de longitud como ICollection través de una propiedad Count . Si no hay ninguno disponible, enumerará todos los elementos y los contará que tienen una complejidad de O (N).

Por ejemplo

 int[] list = CreateSomeList(); Console.WriteLine(list.Length); // O(1) IEnumerable e1 = list; Console.WriteLine(e1.Count()); // O(1) IEnumerable e2 = list.Where(x => x <> 42); Console.WriteLine(e2.Count()); // O(N) 

El valor e2 se implementa como un iterador C # que no admite el recuento O (1) y, por lo tanto, el método Count debe enumerar toda la colección para determinar cuánto tiempo es.

Poco añadido al comentario de Jon Skeet .

Aquí está el código fuente del método de extensión Count() :

.NET 3:

 public static int Count(this IEnumerable source) { if (source == null) { throw Error.ArgumentNull("source"); } ICollection is2 = source as ICollection; if (is2 != null) { return is2.Count; } int num = 0; using (IEnumerator enumerator = source.GetEnumerator()) { while (enumerator.MoveNext()) { num++; } } return num; } 

.NET 4:

 public static int Count(this IEnumerable source) { if (source == null) { throw Error.ArgumentNull("source"); } ICollection is2 = source as ICollection; if (is2 != null) { return is2.Count; } ICollection is3 = source as ICollection; if (is3 != null) { return is3.Count; } int num = 0; using (IEnumerator enumerator = source.GetEnumerator()) { while (enumerator.MoveNext()) { num++; } } return num; } 
  • La longitud es una propiedad fija, por ejemplo, de una única matriz o serie dimensional. Por lo tanto, nunca es necesaria una operación de recuento (las matrices multidimensionales tienen un tamaño de todas las dimensiones multiplicado). La operación O (1) aquí significa que el tiempo de recuperación es siempre el mismo, sin importar cuántos elementos haya. Una búsqueda lineal sería (opuesta a esto) O (n).

  • La propiedad Count en ICollections (List and List , por ejemplo) puede cambiar, por lo que debe actualizarse en las operaciones de Agregar o quitar, o cuando se solicita el recuento después de que la colección ha cambiado. Depende de la implementación del objeto.

  • El método Count () de LINQ básicamente itera CADA VEZ que se llama (excepto cuando el objeto es un tipo ICollection, entonces se solicita la propiedad ICollection.Count).

Tenga en cuenta que IEnumerables a menudo no son colecciones de objetos definidos (como listas, matrices, tablas hash, etc.), sino que se vinculan a operaciones en segundo plano, que generan resultados cada vez que se solicitan (lo que se denomina ejecución diferida).

Por lo general, tiene una instrucción SQL como LINQ como esta (la aplicación típica de la ejecución diferida):

 IEnumerable deptLeaders = from p in persons join d in departments on p.ID equals d.LeaderID orderby p.LastName, p.FirstName select p; 

Entonces, hay un código como este:

 if (deptLeaders.Count() > 0) { ReportNumberOfDeptLeaders(deptLeaders.Count()); if (deptLeaders.Count() > 20) WarnTooManyDepartmentLeaders(deptLeaders.Count()); } 

Entonces, cuando se emite una advertencia para demasiados Líderes de Departamento, .NET pasa CUATRO VECES a través de las personas, los compara con los líderes del departamento, los ordena por su nombre y luego cuenta los objetos resultantes.

Y esto solo ocurre cuando las personas y los departamentos son colecciones de valores preestablecidos, no consultas en sí mismas.