Contar los elementos de un IEnumerable sin la iteración?


private IEnumerable<string> Tables
{
    get
    {
        yield return "Foo";
        yield return "Bar";
    }
}

Digamos que quiero iterar en esos y escribir algo como procesando #n de # m.

¿Hay alguna forma en que pueda averiguar el valor de m sin iterar antes de mi iteración principal?

Espero haber sido claro.

Author: Erik Philips, 2008-10-04

19 answers

IEnumerable no apoya esto. Esto es por diseño. IEnumerable utiliza la evaluación perezosa para obtener los elementos que solicita justo antes de necesitarlos.

Si desea conocer el número de elementos sin iterar sobre ellos, puede usar ICollection<T>, tiene una propiedad Count.

 279
Author: Mendelt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-03 03:02:20

El método de extensión System.Linq.Enumerable.Count en IEnumerable<T> tiene la siguiente implementación:

ICollection<T> c = source as ICollection<TSource>;
if (c != null)
    return c.Count;

int result = 0;
using (IEnumerator<T> enumerator = source.GetEnumerator())
{
    while (enumerator.MoveNext())
        result++;
}
return result;

Así que intenta convertir a ICollection<T>, que tiene una propiedad Count, y usa eso si es posible. De lo contrario itera.

Así que su mejor apuesta es utilizar el método de extensión Count() en su objeto IEnumerable<T>, ya que obtendrá el mejor rendimiento posible de esa manera.

 175
Author: Daniel Earwicker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-02 01:20:57

Simplemente agregando información adicional:

La extensión Count() no siempre itera. Considere Linq a Sql, donde el recuento va a la base de datos, pero en lugar de traer de vuelta todas las filas, emite el comando Sql Count() y devuelve ese resultado en su lugar.

Además, el compilador (o runtime) es lo suficientemente inteligente como para llamar al método objects Count() si tiene uno. Así que es no como dicen otros respondedores, siendo completamente ignorantes y siempre iterando para contar elemento.

En muchos casos donde el programador está comprobando if( enumerable.Count != 0 ) usando el método de extensión Any(), como en if( enumerable.Any() ) es mucho más eficiente con la evaluación perezosa de linq, ya que puede provocar un cortocircuito una vez que puede determinar que hay elementos. También es más legible

 77
Author: Robert Paulson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-29 17:18:31

Iumerable no puede contar sin iterar.

En circunstancias "normales", sería posible para las clases que implementanEnumerable oEnumerable, como List, implementar el método Count devolviendo la Lista.Propiedad Count. Sin embargo, el método Count no es en realidad un método definido en la interfaz oEnumerable. (El único que lo es, de hecho, es GetEnumerator.) Y esto significa que no se puede proporcionar una implementación específica de clase para se.

Más bien, Count it es un método de extensión, definido en la clase estática Enumerable. Esto significa que puede ser invocado en cualquier instancia de una clase derivada , independientemente de la implementación de esa clase. Pero también significa que se implementa en un solo lugar, externo a cualquiera de esas clases. Lo que, por supuesto, significa que debe implementarse de una manera que sea completamente independiente de las funciones internas de estas clases. La única manera de hacer el conteo es a través de la iteración.

 10
Author: Chris Ammerman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-03 21:40:29

Un amigo mío tiene una serie de entradas de blog que proporcionan una ilustración de por qué no puedes hacer esto. Crea una función que devuelve unerableumerable donde cada iteración devuelve el siguiente número primo, hasta ulong.MaxValue, y el siguiente elemento no se calcula hasta que lo solicite. Pregunta rápida: ¿cuántos artículos se devuelven?

Aquí están los mensajes, pero son un poco largos:

  1. Beyond Loops (proporciona una clase EnumerableUtility inicial utilizada en el otro mensajes)
  2. Aplicaciones de Iteración (Implementación inicial)
  3. Métodos de extensión locos: ToLazyList (Optimizaciones de rendimiento)
 9
Author: Joel Coehoorn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-04 15:10:07

No, no en general. Un punto en el uso de enumerables es que el conjunto real de objetos en la enumeración no se conoce (de antemano, o incluso en absoluto).

 8
Author: JesperE,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-03 21:07:40

Puede usar el Sistema.Linq.

using System;
using System.Collections.Generic;
using System.Linq;

public class Test
{
    private IEnumerable<string> Tables
    {
        get {
             yield return "Foo";
             yield return "Bar";
         }
    }

    static void Main()
    {
        var x = new Test();
        Console.WriteLine(x.Tables.Count());
    }
}

Obtendrás el resultado '2'.

 8
Author: prosseek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-26 19:02:13

Alternativamente puede hacer lo siguiente:

Tables.ToList<string>().Count;
 7
Author: Anatoliy Nikolaev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-24 05:50:43

Yendo más allá de su pregunta inmediata (que ha sido completamente respondida en forma negativa), si está buscando reportar el progreso mientras procesa un enumerable, es posible que desee mirar mi entrada de blog Reportando el progreso Durante las Consultas de Linq.

Te permite hacer esto:

BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += (sender, e) =>
      {
          // pretend we have a collection of 
          // items to process
          var items = 1.To(1000);
          items
              .WithProgressReporting(progress => worker.ReportProgress(progress))
              .ForEach(item => Thread.Sleep(10)); // simulate some real work
      };
 5
Author: Samuel Jack,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-05-12 15:56:18

Utilicé tal manera dentro de un método para verificar el contenido pasado en IEnumberable

if( iEnum.Cast<Object>().Count() > 0) 
{

}

Dentro de un método como este:

GetDataTable(IEnumberable iEnum)
{  
    if (iEnum != null && iEnum.Cast<Object>().Count() > 0) //--- proceed further

}
 4
Author: Shahidul Haque,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-29 07:46:52

Depende de qué versión de.Net y la implementación de su objetoerableumerable. Microsoft ha solucionado el IEnumerable.Count método para comprobar la implementación, y utiliza la ICollection.Count o ICollection.Conde, ver detalles aquí https://connect.microsoft.com/VisualStudio/feedback/details/454130

Y abajo está el MSIL de Ildasm para System.Núcleo, en el que el Sistema.Linq reside.

.method public hidebysig static int32  Count<TSource>(class 

[mscorlib]System.Collections.Generic.IEnumerable`1<!!TSource> source) cil managed
{
  .custom instance void System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       85 (0x55)
  .maxstack  2
  .locals init (class [mscorlib]System.Collections.Generic.ICollection`1<!!TSource> V_0,
           class [mscorlib]System.Collections.ICollection V_1,
           int32 V_2,
           class [mscorlib]System.Collections.Generic.IEnumerator`1<!!TSource> V_3)
  IL_0000:  ldarg.0
  IL_0001:  brtrue.s   IL_000e
  IL_0003:  ldstr      "source"
  IL_0008:  call       class [mscorlib]System.Exception System.Linq.Error::ArgumentNull(string)
  IL_000d:  throw
  IL_000e:  ldarg.0
  IL_000f:  isinst     class [mscorlib]System.Collections.Generic.ICollection`1<!!TSource>
  IL_0014:  stloc.0
  IL_0015:  ldloc.0
  IL_0016:  brfalse.s  IL_001f
  IL_0018:  ldloc.0
  IL_0019:  callvirt   instance int32 class [mscorlib]System.Collections.Generic.ICollection`1<!!TSource>::get_Count()
  IL_001e:  ret
  IL_001f:  ldarg.0
  IL_0020:  isinst     [mscorlib]System.Collections.ICollection
  IL_0025:  stloc.1
  IL_0026:  ldloc.1
  IL_0027:  brfalse.s  IL_0030
  IL_0029:  ldloc.1
  IL_002a:  callvirt   instance int32 [mscorlib]System.Collections.ICollection::get_Count()
  IL_002f:  ret
  IL_0030:  ldc.i4.0
  IL_0031:  stloc.2
  IL_0032:  ldarg.0
  IL_0033:  callvirt   instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<!!TSource>::GetEnumerator()
  IL_0038:  stloc.3
  .try
  {
    IL_0039:  br.s       IL_003f
    IL_003b:  ldloc.2
    IL_003c:  ldc.i4.1
    IL_003d:  add.ovf
    IL_003e:  stloc.2
    IL_003f:  ldloc.3
    IL_0040:  callvirt   instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
    IL_0045:  brtrue.s   IL_003b
    IL_0047:  leave.s    IL_0053
  }  // end .try
  finally
  {
    IL_0049:  ldloc.3
    IL_004a:  brfalse.s  IL_0052
    IL_004c:  ldloc.3
    IL_004d:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
    IL_0052:  endfinally
  }  // end handler
  IL_0053:  ldloc.2
  IL_0054:  ret
} // end of method Enumerable::Count
 3
Author: prabug,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-29 00:13:02

Aquí hay una gran discusión sobre evaluación perezosa y ejecución diferida. Básicamente tienes que materializar la lista para obtener ese valor.

 2
Author: JP Alioto,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-05-12 15:41:50

Resultado de la IEnumerable.La función Count () puede estar equivocada. Esta es una muestra muy simple de probar:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections;

namespace Test
{
  class Program
  {
    static void Main(string[] args)
    {
      var test = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 };
      var result = test.Split(7);
      int cnt = 0;

      foreach (IEnumerable<int> chunk in result)
      {
        cnt = chunk.Count();
        Console.WriteLine(cnt);
      }
      cnt = result.Count();
      Console.WriteLine(cnt);
      Console.ReadLine();
    }
  }

  static class LinqExt
  {
    public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> source, int chunkLength)
    {
      if (chunkLength <= 0)
        throw new ArgumentOutOfRangeException("chunkLength", "chunkLength must be greater than 0");

      IEnumerable<T> result = null;
      using (IEnumerator<T> enumerator = source.GetEnumerator())
      {
        while (enumerator.MoveNext())
        {
          result = GetChunk(enumerator, chunkLength);
          yield return result;
        }
      }
    }

    static IEnumerable<T> GetChunk<T>(IEnumerator<T> source, int chunkLength)
    {
      int x = chunkLength;
      do
        yield return source.Current;
      while (--x > 0 && source.MoveNext());
    }
  }
}

El resultado debe ser (7,7,3,3) pero el resultado real es (7,7,3,17)

 2
Author: Roman Golubin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-12 16:41:30

La única manera de tener un conteo rápido es cuando la colección original tiene un indexador (como array). Con el fin de crear código genérico con un requisito mínimo que podría utilizarerableumerable, pero si necesita el recuento también entonces mi forma preferida es utilizar esta interfaz:


    public interface IEnumAndCount<out T> : IEnumerable<T>
    {
        int Count { get; }
    }

Si su colección original no tiene ningún indexador, su implementación Count podría iterar sobre la colección, con el golpe conocido en rendimiento O(n).

Si no quieres usar algo similar a umUmandcount, tu mejor apuesta es ir con Linq.Cuenta por las razones dadas por Daniel Earwicker cerca de la parte superior de esta pregunta.

¡Buena suerte !

 1
Author: Eric Ouellet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-10 17:39:53

No.

¿Ves esa información disponible en cualquier parte del código que has escrito?

Podría argumentar que el compilador puede "ver" que solo hay dos, pero eso significaría que necesitaría analizar cada método iterador buscando solo ese caso patológico específico. Y aún si lo hiciera, ¿cómo la has leído, teniendo en cuenta los límites de un IEnumerable?

 0
Author: James Curran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-03 21:10:41

Sugeriría llamar a ToList. Sí, está haciendo la enumeración temprano, pero todavía tiene acceso a su lista de elementos.

 0
Author: Jonathan Allen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-04 05:00:20

Puede Que no el mejor rendimiento, pero puede usar LINQ para contar los elementos de un IEnumerable:

public int GetEnumerableCount(IEnumerable Enumerable)
{
    return (from object Item in Enumerable
            select Item).Count();
}
 0
Author: Hugo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-26 08:39:09

Utilizo IEnum<string>.ToArray<string>().Length y funciona bien.

 -2
Author: Oliver Kötter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-18 09:58:00

Utilizo dicho código, si tengo lista de cadenas:

((IList<string>)Table).Count
 -2
Author: Me Hungry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-07 10:27:49