Inspirado por otra pregunta sobre la Zip
función que falta :
¿Por qué no hay un ForEach
método de extensión en la Enumerable
clase? O en cualquier lugar? La única clase que obtiene un ForEach
método es List<>
. ¿Hay alguna razón por la que falta (rendimiento)?
c#
.net
vb.net
extension-methods
Cameron MacFarland
fuente
fuente
Parallel.ForEach
paraEnumerable.ForEach
sólo para descubrir esta última no existe. C # se perdió un truco para facilitar las cosas aquí.Respuestas:
Ya hay una declaración foreach incluida en el lenguaje que hace el trabajo la mayor parte del tiempo.
Odiaría ver lo siguiente:
En vez de:
Este último es más claro y más fácil de leer en la mayoría de las situaciones , aunque quizás sea un poco más largo para escribir.
Sin embargo, debo admitir que cambié mi postura sobre ese tema; un método de extensión ForEach () sería realmente útil en algunas situaciones.
Estas son las principales diferencias entre la declaración y el método:
Esos son todos grandes puntos hechos por muchas personas aquí y puedo ver por qué las personas no tienen la función. No me importaría que Microsoft agregue un método ForEach estándar en la próxima iteración del marco.
fuente
foreach
se verifica el tipo en tiempo de compilación si el enumerable está parametrizado. Solo le falta la comprobación del tipo de tiempo de compilación para colecciones no genéricas, como lo haría unForEach
método de extensión hipotético .col.Where(...).OrderBy(...).ForEach(...)
. Sintaxis mucho más simple, sin contaminación de pantalla con variables locales redundantes o corchetes largos en foreach ().list.ForEach(item => item.DoSomething())
. ¿Y quién dice que es una lista? El caso de uso obvio es al final de una cadena; con la declaración foreach, tendría que colocar toda la cadena despuésin
, y la operación a realizar dentro del bloque, que es mucho menos natural o consistente.El método ForEach se agregó antes de LINQ. Si agrega la extensión ForEach, nunca se llamará para instancias de Lista debido a restricciones de métodos de extensión. Creo que la razón por la que no se agregó es para no interferir con el existente.
Sin embargo, si realmente echa de menos esta pequeña y agradable función, puede implementar su propia versión
fuente
Podrías escribir este método de extensión:
Pros
Permite encadenar:
Contras
En realidad, no hará nada hasta que haga algo para forzar la iteración. Por esa razón, no debería llamarse
.ForEach()
. Podrías escribir.ToList()
al final, o también podría escribir este método de extensión: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.
fuente
{foreach (var e in source) {action(e);} return source;}
numbers.Select(n => n*2);
Select()
dice aplicar una función a cada elemento y devolver el valor de la función dondeApply()
dice aplicar unaAction
a cada elemento y devolver el elemento original .Select(e => { action(e); return e; })
La discusión aquí da la respuesta:
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 usar los métodos de extensión Enumerable, que no era la intención.
fuente
Si bien estoy de acuerdo en que es mejor usar la construcción incorporada
Ejemploforeach
en la mayoría de los casos, creo que el uso de esta variación en la extensión ForEach <> es un poco mejor que tener que administrar el índice de forma regularforeach
:Te daría:
fuente
Una solución alternativa es escribir
.ToList().ForEach(x => ...)
.pros
Fácil de entender: el lector solo necesita saber qué se incluye con C #, no ningún método de extensión adicional.
El ruido sintáctico es muy leve (solo agrega un poco de código extranjero).
Por lo general, no cuesta memoria adicional, ya que un nativo
.ForEach()
tendría que realizar toda la colección, de todos modos.contras
El orden de las operaciones no es ideal. Prefiero darme cuenta de un elemento, luego actuar sobre él, luego repetir. Este código realiza primero todos los elementos, luego actúa sobre cada uno de ellos en secuencia.
Si darse cuenta de que la lista arroja una excepción, nunca puede actuar sobre un solo elemento.
Si la enumeración es infinita (como los números naturales), no tienes suerte.
fuente
Enumerable.ForEach<T>
método, sí existe unList<T>.ForEach
método. c) "Por lo general, no cuesta memoria adicional, ya que un nativo .ForEach () tendría que realizar toda la colección de todos modos". - Completa y absoluta tontería. Aquí está la implementación de Enumerable.ForEach <T> que C # proporcionaría si lo hiciera:public static void ForEach<T>(this IEnumerable<T> list, Action<T> action) { foreach (T item in list) action(item); }
Siempre me lo he preguntado, es por eso que siempre llevo esto conmigo:
Pequeño y agradable método de extensión.
fuente
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 declaración de hechos, no es del todo cierto.
Todos los métodos de extensión LINQ devuelven un valor para que puedan encadenarse:
Sin embargo, el hecho de que LINQ se implemente utilizando métodos de extensión no significa que los métodos de extensión deben usarse 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 sobre 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 imponen 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 de herencia.fuente
Puede usar el (encadenable, pero evaluado perezosamente)
Select
, primero haciendo su operación y luego devolviendo la identidad (o algo más si lo prefiere)Deberá asegurarse de que todavía se evalúa, ya sea con
Count()
(la operación más barata para enumerar afaik) u otra operación que necesita de todos modos.Sin embargo, me encantaría verlo en la biblioteca estándar:
El código anterior se convierte en lo
people.WithLazySideEffect(p => Console.WriteLine(p))
que es efectivamente equivalente a foreach, pero vago y encadenable.fuente
Select
ya no hace lo que se supone que debe hacer. definitivamente es una solución para lo que pide el OP, pero en cuanto a la legibilidad del tema: nadie esperaría que la selección ejecute una función.Select
no hace lo que se supone que debe hacer, es un problema con la implementaciónSelect
, no con el código que llamaSelect
, que no tiene influencia sobre lo queSelect
hace.Tenga en cuenta que MoreLINQ NuGet proporciona el
ForEach
método de extensión que está buscando (así como unPipe
método que ejecuta el delegado y produce su resultado). Ver:fuente
@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 una función correspondiente no cabe en 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, puede mantener toda su lógica en un solo lugar (elAction<>
. Entonces, docenas de líneas se reemplazan conetc ...
La lógica está en un lugar y no has contaminado tu clase.
fuente
f(p)
, a continuación, dejar de lado el{ }
de la taquigrafía:for (var p in List1.Concat(List2).Concat(List2)) f(p);
.La mayoría de los métodos de extensión LINQ devuelven resultados. ForEach no se ajusta a este patrón ya que no devuelve nada.
fuente
public IEnumerable<T> Foreach<T>(this IEnumerable<T> items, Action<T> action) { /* error check */ foreach (var item in items) { action(item); yield return item; } }
Si tiene F # (que estará en la próxima versión de .NET), puede usar
Seq.iter doSomething myIEnumerable
fuente
Parcialmente es porque los diseñadores del lenguaje no están de acuerdo con él desde una perspectiva filosófica.
https://blogs.msdn.microsoft.com/ericlippert/2009/05/18/foreach-vs-foreach/
fuente
Puedes usar select cuando quieras devolver algo. Si no lo hace, puede usar ToList primero, porque probablemente no quiera modificar nada en la colección.
fuente
Escribí una publicación de blog al respecto: http://blogs.msdn.com/kirillosenkov/archive/2009/01/31/foreach.aspx
Puede votar aquí si desea ver este método en .NET 4.0: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=279093
fuente
En 3.5, todos los métodos de extensión agregados a IEnumerable están ahí para el soporte de LINQ (tenga en cuenta que están definidos en la clase System.Linq.Enumerable). En esta publicación, explico por qué foreach no pertenece a LINQ: método de extensión LINQ existente similar a Parallel.For?
fuente
¿Soy yo o la Lista <T>? Por lo tanto, Linq ha dejado obsoleta. Originalmente hubo
donde Y simplemente tenía que ser IEnumerable (Pre 2.0) e implementar un GetEnumerator (). Si observa el MSIL generado, puede ver que es exactamente igual a
(Ver http://alski.net/post/0a-for-foreach-forFirst-forLast0a-0a-.aspx para el MSIL)
Luego, en DotNet2.0, aparecieron 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 una Lambda con el mismo efecto, por ejemplo, intente http://dotnet-developments.blogs.techtarget.com/2008/09/02/iterators-lambda-and-linq-oh- mi/
fuente
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:
fuente
Select(x => { f(x); return x;})
Nadie ha señalado aún que ForEach <T> resulta en la verificación del tipo de tiempo de compilación donde se verifica la palabra clave foreach en tiempo de ejecución.
Después de realizar algunas refactorizaciones donde se utilizaron ambos métodos en el código, estoy a favor de .ForEach, ya que tuve que buscar fallas de prueba / fallas de tiempo de ejecución para encontrar los problemas foreach.
fuente
foreach
tiene menos tiempo de compilación comprobando. Claro, si su colección es un IEnumerable no genérico, la variable de bucle será unobject
, pero lo mismo sería cierto para unForEach
método de extensión hipotético .