que - recorrer un ienumerable c#




¿Por qué no hay un método de extensión ForEach en IEnumerable? (14)

Inspirado en otra pregunta sobre la función Zip faltante:

¿Por qué no hay ForEach método de extensión ForEach en la clase Enumerable ? O en cualquier parte? La única clase que obtiene un método ForEach es List<> . ¿Hay alguna razón por la que falta (rendimiento)?


¿Soy yo o es la Lista <T>. Forqne prácticamente ha quedado obsoleta por Linq. Originalmente hubo

foreach(X x in Y) 

donde Y simplemente tenía que ser IEnumerable (Pre 2.0) e implementar un GetEnumerator (). Si miras la MSIL generada, puedes ver que es exactamente igual a

IEnumerator<int> enumerator = list.GetEnumerator();
while (enumerator.MoveNext())
{
    int i = enumerator.Current;

    Console.WriteLine(i);
}

(Consulte http://alski.net/post/0a-for-foreach-forFirst-forLast0a-0a-.aspx para MSIL)

Luego, en DotNet2.0 llegaron los genéricos y la lista. Foreach siempre me ha parecido una implementación del patrón Vistor, (ver Patrones de diseño de Gamma, Helm, Johnson, Vlissides).

Ahora, por supuesto, en 3.5 podemos usar un Lambda para el mismo efecto, por ejemplo, http://dotnet-developments.blogs.techtarget.com/2008/09/02/iterators-lambda-and-linq-oh-my/


@Coincoin

El poder real del método de extensión foreach implica la reutilización de la Action<> sin agregar métodos innecesarios a su código. Digamos que tiene 10 listas y desea realizar la misma lógica en ellas, y la función correspondiente no se ajusta a su clase y no se reutiliza. En lugar de tener diez para bucles, o una función genérica que obviamente es una ayuda que no pertenece, puedes mantener toda tu lógica en un lugar (la Action<> . Así, docenas de líneas se reemplazan con

Action<blah,blah> f = { foo };

List1.ForEach(p => f(p))
List2.ForEach(p => f(p))

etc ...

La lógica está en un solo lugar y no has contaminado a tu clase.


El método ForEach fue agregado antes de LINQ. Si agrega la extensión ForEach, nunca se llamará para instancias de lista debido a las restricciones de los métodos de extensión. Creo que la razón por la que no se agregó es para no interferir con uno existente.

Sin embargo, si realmente echas de menos esta pequeña y agradable función, puedes lanzar tu propia versión

public static void ForEach<T>(
    this IEnumerable<T> source,
    Action<T> action)
{
    foreach (T element in source) 
        action(element);
}

En 3.5, todos los métodos de extensión agregados a IEnumerable están ahí para el soporte de LINQ (observe que están definidos en la clase System.Linq.Enumerable). En esta publicación, explico por qué foreach no pertenece a LINQ: ¿ Existe un método de extensión LINQ similar a Parallel.For?



La mayoría de los métodos de extensión LINQ devuelven resultados. ForEach no encaja en este patrón ya que no devuelve nada.


Nadie ha señalado aún que ForEach <T> da como resultado la comprobación de los tipos de tiempo de compilación donde se verifica la palabra clave foreach en tiempo de ejecución.

Habiendo hecho un poco de refactorización donde se usaron ambos métodos en el código, estoy a favor de .ForEach, ya que tuve que buscar fallos de prueba / fallos de tiempo de ejecución para encontrar los problemas de foreach.


Para usar Foreach, tu lista debe estar cargada en la memoria primaria. Pero debido a la naturaleza de carga perezosa de IEnumerable, no proporcionan ForEach en IEnumerable.

Sin embargo, al cargar con entusiasmo (usando ToList ()), puede cargar su lista en la memoria y aprovechar ForEach.


Puede usar la Selección (chainable, pero perezosamente evaluada), primero haciendo su operación y luego devolviendo la identidad (o algo más si lo prefiere)

IEnumerable<string> people = new List<string>(){"alica", "bob", "john", "pete"};
people.Select(p => { Console.WriteLine(p); return p; });

Deberá asegurarse de que aún se evalúa, ya sea con Count() (la operación más barata para enumerar afaik) u otra operación que necesite de todos modos.

Aunque me encantaría verlo en la biblioteca estándar:

static IEnumerable<T> WithLazySideEffect(this IEnumerable<T> src, Action<T> action) {
  return src.Select(i => { action(i); return i; } );
}

El código anterior se convierte en people.WithLazySideEffect(p => Console.WriteLine(p)) que es efectivamente equivalente a foreach, pero perezoso y chainable.


Puedes usar seleccionar cuando quieras devolver algo. Si no lo hace, puede usar ToList primero, porque probablemente no quiera modificar nada en la colección.


Si tiene F # (que estará en la próxima versión de .NET), puede usar

Seq.iter doSomething myIEnumerable


Siempre me he preguntado eso, por eso siempre llevo esto conmigo:

public static void ForEach<T>(this IEnumerable<T> col, Action<T> action)
{
    if (action == null)
    {
        throw new ArgumentNullException("action");
    }
    foreach (var item in col)
    {
        action(item);
    }
}

Buen pequeño método de extensión.


Una solución es escribir .ToList().ForEach(x => ...) .

pros

Fácil de entender: el lector solo necesita saber qué incluye C #, no ningún método de extensión adicional.

El ruido sintáctico es muy suave (solo agrega un poco de código extranioso).

No suele costar más memoria, ya que un nativo .ForEach() tendría que realizar la colección completa, de todos modos.

contras

El orden de las operaciones no es ideal. Prefiero comprender un elemento, luego actuar sobre él y luego repetir. Este código realiza todos los elementos primero, luego actúa sobre cada uno en secuencia.

Si darse cuenta de que la lista produce una excepción, nunca podrá actuar en un solo elemento.

Si la enumeración es infinita (como los números naturales), no tienes suerte.


Ya hay una declaración de foreach incluida en el idioma que hace el trabajo la mayor parte del tiempo.

Odiaría ver lo siguiente:

list.ForEach( item =>
{
    item.DoSomething();
} );

En lugar de:

foreach(Item item in list)
{
     item.DoSomething();
}

Este último es más claro y fácil de leer en la mayoría de las situaciones , aunque quizás sea un poco más largo de escribir.

Sin embargo, debo admitir que cambié mi postura sobre ese tema; un método de extensión ForEach () de hecho sería útil en algunas situaciones.

Aquí están las principales diferencias entre la declaración y el método:

  • Comprobación de tipos: foreach se realiza en tiempo de ejecución, ForEach () está en tiempo de compilación (Big Plus!)
  • La sintaxis para llamar a un delegado es mucho más simple: objects.ForEach (DoSomething);
  • ForEach () podría estar encadenado: aunque la maldad / utilidad de tal característica está abierta a discusión.

Esas son todas las grandes observaciones hechas por muchas personas aquí y puedo ver por qué a las personas les falta la función. No me importaría que Microsoft agregara un método ForEach estándar en la siguiente iteración del marco.





extension-methods