valores una recorrer obtener lista index elemento buscar c# .net ienumerable

c# - una - Cuente los elementos de un<Enumerable ITumerable sin iterar?



obtener valores de una lista c# (19)

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

Digamos que quiero iterar sobre ellos y escribir algo como procesar #n de #m.

¿Hay alguna manera de averiguar el valor de m sin iterar antes de mi iteración principal?

Espero aclararme.


Alternativamente puedes hacer lo siguiente:

Tables.ToList<string>().Count;



Depende de la versión de .Net y la implementación de su objeto IEnumerable. Microsoft ha corregido el método IEnumerable.Count para verificar la implementación, y utiliza ICollection.Count o ICollection <TSource> .Count, consulte los detalles aquí https://connect.microsoft.com/VisualStudio/feedback/details/454130

Y debajo está 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


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;

Por lo tanto, intenta convertir a ICollection<T> , que tiene una propiedad Count , y la usa si es posible. De lo contrario itera.

Por lo tanto, lo mejor es usar el método de extensión Count() en su objeto IEnumerable<T> , ya que obtendrá el mejor rendimiento posible de esa manera.


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)


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 devolviendo 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 es, de hecho, es GetEnumerator). Y esto significa que no se le 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 puede invocarse en cualquier instancia de una clase derivada de 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 los aspectos internos de esta clase. La única forma de hacerlo es mediante iteración.


La única manera de tener un recuento rápido es cuando la colección original tiene un indexador (como matriz). Para crear un código genérico con un requisito mínimo, puede usar IEnumerable, pero si también necesita el conteo, mi forma preferida es usar 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 de Recuento podría iterar sobre la colección, con el golpe conocido en el rendimiento O (n).

Si no quiere usar algo similar a IEnumAndCount, lo mejor es ir con Linq.Count por razones dadas por Daniel Earwicker cerca de la parte superior de esta pregunta.

Buena suerte !


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 (por adelantado, o incluso en absoluto).


No.

¿Ves esa información disponible en algún lugar del código que has escrito?

Podría argumentar que el compilador puede "ver" que solo hay dos, pero eso significaría que tendría que analizar cada método de iterador en busca de ese caso patológico específico. E incluso si lo hiciera, ¿cómo lo leerías, dados los límites de un IEnumerable?


Puede 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(); }


Puedes 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''.


Sólo añadiendo un poco de 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 recuperar todas las filas, emite el comando Sql Count() y devuelve ese resultado.

Además, el compilador (o tiempo de ejecución) es lo suficientemente inteligente como para llamar al método Count() los objetos si tiene uno. Así que no es como dicen otros respondedores, ser completamente ignorante y siempre iterar para contar elementos.

En muchos casos, el programador solo está verificando 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 cortocircuitar Una vez que se puede determinar hay algún elemento. También es más legible


Si va más allá de su pregunta inmediata (que ha sido respondida en forma negativa), si desea informar sobre el progreso mientras procesa un número enumerable, puede consultar la publicación de mi blog Informar sobre 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 };


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


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 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 las publicaciones, pero son un poco largas:

  1. Beyond Loops (proporciona una clase de EnumerableUtility inicial utilizada en las otras publicaciones)
  2. Aplicaciones de Iterate (Implementación inicial)
  3. Métodos de extensión loca: ToLazyList (optimizaciones de rendimiento)

Utilicé de esa manera dentro de un método para verificar el contenido de IEnumberable 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 }


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

((IList<string>)Table).Count


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


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

Si desea saber la cantidad de elementos sin iterar sobre ellos, puede usar ICollection<T> , tiene una propiedad Count .