recorrer que multiple columns c# .net vb.net extension-methods

c# - que - ¿Por qué no hay un método de extensión ForEach en IEnumerable?



order by linq c# multiple columns (21)

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.


Así que ha habido muchos comentarios sobre el hecho de que un método de extensión ForEach no es apropiado porque no devuelve un valor como los métodos de extensión LINQ. Si bien esta es una afirmación objetiva, no es del todo cierto.

Todos los métodos de extensión de LINQ devuelven un valor para que se puedan encadenar juntos:

collection.Where(i => i.Name = "hello").Select(i => i.FullName);

Sin embargo, solo porque LINQ se implementa utilizando métodos de extensión no significa que los métodos de extensión se deben usar de la misma manera y devolver un valor. Escribir un método de extensión para exponer una funcionalidad común que no devuelve un valor es un uso perfectamente válido.

El argumento específico acerca de ForEach es que, en función de las restricciones en los métodos de extensión (es decir, que un método de extensión nunca anulará un método heredado con la misma firma ), puede haber una situación en la que el método de extensión personalizado esté disponible en todas las clases que se impulsen IEnumerable <T > excepto Lista <T >. Esto puede causar confusión cuando los métodos comienzan a comportarse de manera diferente dependiendo de si se llama o no al método de extensión o al método heredado.


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?


En parte es porque los diseñadores de lenguaje no están de acuerdo con él desde una perspectiva filosófica.

  • No tener (y probar ...) una característica es menos trabajo que tener una característica.
  • No es realmente más corto (hay algunos casos de funciones de paso, pero no sería el uso principal).
  • Su propósito es tener efectos secundarios, que no es de lo que se trata linq.
  • ¿Por qué tenemos otra forma de hacer lo mismo que una característica que ya tenemos? (palabra clave foreach)

https://blogs.msdn.microsoft.com/ericlippert/2009/05/18/foreach-vs-foreach/



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.


Me gustaría ampliar la respuesta de Aku .

Si desea llamar a un método con el único propósito de su efecto secundario sin iterar todo el enumerable primero, puede usar esto:

private static IEnumerable<T> ForEach<T>(IEnumerable<T> xs, Action<T> f) { foreach (var x in xs) { f(x); yield return x; } }


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.


Podrías escribir este método de extensión:

// Possibly call this "Do" IEnumerable<T> Apply<T> (this IEnumerable<T> source, Action<T> action) { foreach (var e in source) { action(e); yield return e; } }

Pros

Permite el encadenamiento:

MySequence .Apply(...) .Apply(...) .Apply(...);

Contras

Realmente no hará nada hasta que hagas algo para forzar la iteración. Por esa razón, no debería llamarse .ForEach() . Podría escribir .ToList() al final, o también podría escribir este método de extensión:

// possibly call this "Realize" IEnumerable<T> Done<T> (this IEnumerable<T> source) { foreach (var e in source) { // do nothing ; } return source; }

Esto puede ser una desviación demasiado significativa de las bibliotecas C # de envío; los lectores que no estén familiarizados con sus métodos de extensión no sabrán qué hacer con su código.


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 bien estoy de acuerdo en que es mejor usar la construcción incorporada foreach en la mayoría de los casos, me parece que el uso de esta variación en la extensión ForEach <> es un poco más agradable que tener que administrar el índice de una manera regular para cada persona:

public static int ForEach<T>(this IEnumerable<T> list, Action<int, T> action) { if (action == null) throw new ArgumentNullException("action"); var index = 0; foreach (var elem in list) action(index++, elem); return index; } Ejemplo

var people = new[] { "Moe", "Curly", "Larry" }; people.ForEach((i, p) => Console.WriteLine("Person #{0} is {1}", i, p));

Te daria

Person #0 is Moe Person #1 is Curly Person #2 is Larry


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.


La discusión aquí da la respuesta:

En realidad, la discusión específica que presencié, de hecho, dependía de la pureza funcional. En una expresión, frecuentemente se hacen suposiciones acerca de no tener efectos secundarios. Teniendo ForEach es invitar específicamente a los efectos secundarios en lugar de simplemente tolerarlos. - Keith Farmer (Socio)

Básicamente, se tomó la decisión de mantener los métodos de extensión funcionalmente "puros". Un ForEach fomentaría los efectos secundarios al utilizar los métodos de extensión Enumerable, que no era la intención.