Respuestas:
IEnumerable
No es compatible con esto. Esto es por diseño. IEnumerable
utiliza la evaluación diferida para obtener los elementos que solicita justo antes de que los necesite.
Si desea saber la cantidad de elementos sin iterar sobre ellos, puede usarlos ICollection<T>
, tiene una Count
propiedad.
El System.Linq.Enumerable.Count
método de extensión 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;
Por lo tanto, trata de enviar a ICollection<T>
, que tiene una Count
propiedad, y la usa si es posible. De lo contrario, itera.
Por lo tanto, su mejor opción es utilizar el Count()
método de extensión en su IEnumerable<T>
objeto, ya que obtendrá el mejor rendimiento posible de esa manera.
ICollection<T>
primero.
T
de IEnumerator<T> enumerator = source.GetEnumerator()
, simplemente se puede hacer esto: IEnumerator enumerator = source.GetEnumerator()
y debe trabajar!
IEnumerable<T>
hereda, lo IDisposable
que permite que la using
instrucción lo elimine automáticamente. IEnumerable
no. Así que si usted llama GetEnumerator
de cualquier manera, usted debe terminar convar d = e as IDisposable; if (d != null) d.Dispose();
Solo agrego información adicional:
La Count()
extensión no siempre itera. Considere Linq to Sql, donde el recuento va a la base de datos, pero en lugar de recuperar todas las filas, emite el Count()
comando Sql y devuelve ese resultado.
Además, el compilador (o tiempo de ejecución) es lo suficientemente inteligente como para llamar al Count()
método de los objetos si tiene uno. Entonces, no es como dicen otros respondedores, siendo completamente ignorantes y siempre iterando para contar elementos.
En muchos casos, el programador solo está verificando el if( enumerable.Count != 0 )
uso del Any()
método de extensión, ya que if( enumerable.Any() )
es mucho más eficiente con la evaluación diferida de linq, ya que puede causar un cortocircuito una vez que puede determinar que hay elementos. También es más legible
.Count
propiedad, ya que siempre sabe su tamaño. Al consultar collection.Count
no hay cálculo adicional, simplemente devuelve el recuento ya conocido. Lo mismo por Array.length
lo que yo sé. Sin embargo, .Any()
obtiene el enumerador de la fuente usando using (IEnumerator<TSource> enumerator = source.GetEnumerator())
y devuelve verdadero si puede hacerlo enumerator.MoveNext()
. Para colecciones:, if(collection.Count > 0)
matrices: if(array.length > 0)
y en enumerables if(collection.Any())
.
IQueryably<T>
a IEnumerable<T>
, no emitirá un recuento de sql. Cuando escribí esto, Linq to Sql era nuevo; Creo que solo estaba presionando para usar Any()
porque para enumerables, colecciones y sql era mucho más eficiente y legible (en general). Gracias por mejorar la respuesta.
Un amigo mío tiene una serie de publicaciones en el blog que proporcionan una ilustración de por qué no puedes hacer esto. Crea una función que devuelve un IEnumerable donde cada iteración devuelve el siguiente número primo, hasta el final ulong.MaxValue
, y el siguiente elemento no se calcula hasta que lo solicite. Pregunta rápida y emergente: ¿cuántos artículos se devuelven?
Aquí están las publicaciones, pero son algo largas:
EnumerableFeatures
objeto) no requeriría que los enumeradores hicieran nada difícil, pero poder hacer tales preguntas (junto con algunas otras como "¿Puede prometerle? devolver siempre la misma secuencia de elementos "," ¿Puede estar expuesto de forma segura al código que no debería alterar su colección subyacente ", etc.) sería muy útil.
set yield options
declaración o algo similar para respaldarla. Si se diseña adecuadamente, IEnhancedEnumerator
podría hacer que cosas como LINQ sean mucho más útiles al eliminar muchas "defensivas" ToArray
o ToList
llamadas, especialmente ...
Enumerable.Concat
se usan para combinar una gran colección que sabe mucho de sí misma con una pequeña que no.
IEnumerable no puede contar sin iterar.
En circunstancias "normales", sería posible que las clases que implementan IEnumerable o IEnumerable <T>, como List <T>, implementen el método Count al devolver la propiedad List <T> .Count. Sin embargo, el método Count no es realmente un método definido en la interfaz IEnumerable <T> o IEnumerable. (El único que, de hecho, es GetEnumerator). Y esto significa que no se puede proporcionar una implementación específica de clase.
Más bien, Count es un método de extensión, definido en la clase estática Enumerable. Esto significa que se puede invocar en cualquier instancia de una clase derivada IEnumerable <T>, 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 partes internas de estas clases. La única forma de contar es a través de la iteración.
Alternativamente, puede hacer lo siguiente:
Tables.ToList<string>().Count;
No, no en general. Un punto en el uso de enumerables es que no se conoce el conjunto real de objetos en la enumeración (de antemano, o incluso en absoluto).
Puede usar System.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'.
Yendo más allá de su pregunta inmediata (que ha sido completamente respondida en forma negativa), si está buscando informar sobre el progreso mientras procesa una enumerable, es posible que desee ver mi publicación de blog Informe de 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
};
Utilicé tal método dentro de un método para verificar el IEnumberable
contenido pasado
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
}
bool hasContents = false; if (iEnum != null) foreach (object ob in iEnum) { hasContents = true; ... your code per ob ... }
.
Depende de la versión de .Net y la implementación de su objeto IEnumerable. Microsoft ha reparado el método IEnumerable.Count para verificar la implementación, y utiliza ICollection.Count o ICollection <TSource> .Count, vea los detalles aquí https://connect.microsoft.com/VisualStudio/feedback/details/454130
Y a continuación se muestra el MSIL de Ildasm para System.Core, en el que reside System.Linq.
.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
Aquí hay una gran discusión sobre la evaluación diferida y la ejecución diferida . Básicamente tienes que materializar la lista para obtener ese valor.
El resultado de la función IEnumerable.Count () puede ser incorrecto. Esta es una muestra muy simple para 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)
La mejor manera que encontré es contar convirtiéndola en una lista.
IEnumerable<T> enumList = ReturnFromSomeFunction();
int count = new List<T>(enumList).Count;
Sugeriría llamar a ToList. Sí, está haciendo la enumeración temprano, pero aún tiene acceso a su lista de elementos.
Es posible que no produzca el mejor rendimiento, pero puede usar LINQ para contar los elementos en un IEnumerable:
public int GetEnumerableCount(IEnumerable Enumerable)
{
return (from object Item in Enumerable
select Item).Count();
}
Creo que la forma más fácil de hacer esto.
Enumerable.Count<TSource>(IEnumerable<TSource> source)
Referencia: system.linq.enumerable
Yo uso IEnum<string>.ToArray<string>().Length
y funciona bien.
IEnum<string>.Count();
"?
Uso dicho código, si tengo una lista de cadenas:
((IList<string>)Table).Count