La ejecución de una acción determinada para todos los elementos en un Enumerable

StackOverflow https://stackoverflow.com/questions/529188

  •  22-08-2019
  •  | 
  •  

Pregunta

Tengo una Enumerable<T> y estoy buscando un método que me permite ejecutar una acción para cada elemento, una especie de Select pero luego de efectos secundarios. Algo así como:

string[] Names = ...;
Names.each(s => Console.Writeline(s));

o

Names.each(s => GenHTMLOutput(s));   
// (where GenHTMLOutput cannot for some reason receive the enumerable itself as a parameter)

Yo probé Select(s=> { Console.WriteLine(s); return s; }), pero no estaba imprimiendo nada.

¿Fue útil?

Solución

Una forma rápida y fácil-de conseguir esto es:

Names.ToList().ForEach(e => ...);

Otros consejos

Se busca la ParaCada cada vez más difícil que en la actualidad sólo existe en la colección genérica de lista. Hay muchas discusiones en línea sobre si Microsoft debe o no debe añadir esto como un método de LINQ. Actualmente, tiene que rodar su propia:

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

Mientras que el método All() proporciona capacidades similares, es el uso de los casos es para realizar una prueba predicado en cada artículo en lugar de una acción. Por supuesto, puede ser persuadido para realizar otras tareas, pero esto cambia un poco la semántica y haría más difícil que otros para interpretar el código (es decir, es este uso de All() para una prueba predicado o una acción?).

exención de responsabilidad: Este mensaje ya no se parece a mi respuesta original, sino que incorpora la experiencia de unos siete años que he ganado desde entonces. Hice la edición porque esta es una pregunta muy visto, y ninguna de las respuestas existentes realmente cubierto todos los ángulos. Si quieres ver mi respuesta original, que está disponible en el historial de revisiones para este post .


Lo primero que hay que entender aquí es C # linq operaciones como Select(), All(), Where(), etc., tienen sus raíces en la programación funcional. La idea era traer algunas de las partes más útiles y accesibles de la programación funcional al mundo .Net. Esto es importante, ya que un principio clave de la programación funcional es para operaciones están libres de efectos secundarios. Es difícil subestimar esto. Sin embargo, en el caso de ForEach() / each(), los efectos secundarios son toda la puprose de la operación. Añadiendo each() o ForEach() no es justo fuera del alcance de la programación funcional de los otros operadores de LINQ, pero en oposición directa a ellos.

Pero entiendo que esto se siente poco satisfactoria. Usted tiene un problema real que necesita para resolver. ¿Por qué toda esta filosofía torre de marfil en el camino de algo que podría ser realmente útil?

Eric Lippert, que estaba en el equipo de diseño de C # en el momento, nos puede ayudar aquí. Se recomienda el uso de un bucle foreach tradicional:

  

[ParaCada ()] agrega un nuevo poder de representación cero a la lengua. Hacer esto le permite reescribir el código perfectamente claro:

     

foreach(Foo foo in foos){ statement involving foo; }

     

en este código:

     

foos.ForEach(foo=>{ statement involving foo; });

Su punto es, cuando se mira de cerca a sus opciones de sintaxis, no se gana nada nuevo de una extensión ForEach() frente a un bucle foreach tradicional. Estoy parcialmente de acuerdo. Imagine que tiene esto:

 foreach(var item in Some.Long(and => possibly)
                         .Complicated(set => ofLINQ)
                         .Expression(to => evaluate)
 {
     // now do something
 } 

Este código ofusca significado, porque separa la palabra clave foreach de las operaciones en el bucle. También enumera el comando de bucle antes a las operaciones que definen la secuencia en la que funciona el bucle. Se siente mucho más natural querer tener esas operaciones son lo primero, y luego tener el comando del lazo en el final de la definición de la consulta. Además, el código es simplemente feo. Parece que sería mucho más agradable para ser capaz de escribir esto:

Some.Long(and => possibly)
   .Complicated(set => ofLINQ)
   .Expression(to => evaluate)
   .ForEach(item => 
{
    // now do something
});

Sin embargo, incluso en este caso, el tiempo llegué en torno al punto de vista de Eric. Me di cuenta de código como se ve arriba está llamando a una variable adicional. Si usted tiene un complicado conjunto de expresiones LINQ por el estilo, se puede añadir información valiosa a su código asignando primero el resultado de la expresión LINQ a una nueva variable:

var queryForSomeThing = Some.Long(and => possibly)
                        .Complicated(set => ofLINQ)
                        .Expressions(to => evaluate);
foreach(var item in queryForSomeThing)
{
    // now do something
}

Este código se siente más natural. Se pone la palabra clave foreach vuelta junto al resto del bucle, y después de la definición de la consulta. Por encima de todo, el nombre de la variable puede añadir nueva información que será útil para futuros programadores que tratan de entender el propósito de la consulta LINQ. Una vez más, vemos que el operador ForEach() deseada realmente no añadía ninguna fuerza expresiva de la lengua.

Sin embargo, todavía estamos perdiendo dos características de un método de extensión ForEach() hipotética:

  1. No componibles. No puedo añadir un .Where() más o GroupBy() o OrderBy() después de una línea de bucle foreach con el resto del código, sin necesidad de crear una nueva declaración.
  2. No es perezoso. Estas operaciones ocurren inmediatamente. No hace albajo que yo, por ejemplo, tener una forma en que un usuario elige una operación como un campo en una pantalla más grande que no se actúa hasta que el usuario pulsa un botón de comando. Esta forma podría permitir al usuario cambiar de opinión antes de ejecutar el comando. Esto es perfectamente normal ( fácil , incluso) con una consulta LINQ, pero no es tan simple con un foreach.

Se podría, por supuesto, hacer su propio método de extensión ForEach(). Varias otras respuestas tienen implementaciones de este método ya; que no es tan complicado. Sin embargo, me siento como que es innecesario. Ya hay un método existente que se ajuste a lo que queremos hacer desde ambos puntos de vista semántico y operacionales. Tanto de las características que faltan anteriores pueden resolverse mediante el uso del operador Select() existente.

Select() se ajusta a la clase de transformación o proyección descrito por tanto de los ejemplos anteriores. Tenga en cuenta, sin embargo, que aun así, evitar la creación de efectos secundarios. La llamada a Select() debe devolver cualquiera de los objetos nuevos o proyecciones de los originales. Esto a veces puede ser ayudada mediante el uso de un tipo anónimo o objeto dinámico (si y sólo si es necesario). Si necesita los resultados de persistir en, por ejemplo, una variable de la lista original, siempre se puede llamar .ToList() y asignar de nuevo a su variable original. Voy a añadir aquí que yo prefiero trabajar con variables IEnumerable<T> tanto como sea posible sobre los tipos más concretos.

myList = myList.Select(item => new SomeType(item.value1, item.value2 *4)).ToList();

En resumen:

  1. Sólo se adhieren con foreach mayor parte del tiempo.
  2. Cuando foreach realmente no hacerlo (que probablemente no es tan a menudo como usted piensa), el uso Select()
  3. Cuando es necesario utilizar Select(), todavía puede generalmente evitar los efectos secundarios (en programas visibles), posiblemente mediante la proyección de un tipo anónimo.

Desafortunadamente, no hay forma integrada de hacer esto en la versión actual de LINQ. El equipo desatendido marco para añadir un método de extensión .ForEach. Hay una buena discusión sobre este pasando ahora mismo en el siguiente blog.

http://blogs.msdn.com/kirillosenkov/ archivo / 2009/01/31 / foreach.aspx

Es bastante fácil añadir uno sin embargo.

public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action) {
  foreach ( var cur in enumerable ) {
    action(cur);
  }
}

No se puede hacer esto de inmediato con LINQ y IEnumerable - es necesario o bien implementar su propio método de extensión, o emitir su enumeración de una matriz con LINQ y luego llamar Array.ForEach ():

Array.ForEach(MyCollection.ToArray(), x => x.YourMethod());

Tenga en cuenta que debido a los tipos de valores de camino y estructuras de trabajo, si la colección es de un tipo de valor y modificar los elementos de la colección de esta manera, no tendrá ningún efecto sobre los elementos de la colección original.

Debido a LINQ está diseñado para ser una función de consulta y no una función de actualización que no encontrará en una extensión que ejecuta métodos en IEnumerable , porque eso permitirá ejecutar un método (potencialmente con efectos secundarios). En este caso, es posible que también acaba de seguir con

  

foreach (nombre de la cadena de nombres)
  Console.WriteLine (nombre);

Bueno, también puede utilizar la palabra clave foreach estándar, solo formato en un oneliner:

foreach(var n in Names.Where(blahblah)) DoStuff(n);

Lo sentimos, considera que la opción merece estar aquí:)

Hay un método ParaCada fuera de la lista. Se podría convertir el Enumerable a la lista llamando al método .ToList (), y luego llamar al método ParaCada fuera de eso.

Por otra parte, he escuchado de personas que definen su propio método ParaCada fuera de IEnumerable. Esto se puede conseguir llamando esencialmente el método ParaCada, pero en lugar de envolverlo en un método de extensión:

public static class IEnumerableExtensions
{
    public static IEnumerable<T> ForEach<T>(this IEnumerable<T> _this, Action<T> del)
    {
        List<T> list = _this.ToList();
        list.ForEach(del);
        return list;
    }
}

El uso de LINQ paralelo:

Names.AsParallel (). ParaTodos (nombre => ...)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top