Pregunta

  

Posible duplicado:
   ¿Por qué no hay un método de extensión ForEach? en la interfaz IEnumerable?

Cuando escribo el código LINQ-y he notado que .ForEach () es un buen lenguaje para usar. Por ejemplo, aquí hay un fragmento de código que toma las siguientes entradas y produce estas salidas:

{ "One" } => "One"
{ "One", "Two" } => "One, Two"
{ "One", "Two", "Three", "Four" } => "One, Two, Three and Four";

Y el código:

private string InsertCommasAttempt(IEnumerable<string> words)
{
    List<string> wordList = words.ToList();
    StringBuilder sb = new StringBuilder();
    var wordsAndSeparators = wordList.Select((string word, int pos) =>
        {
            if (pos == 0) return new { Word = word, Leading = string.Empty };
            if (pos == wordList.Count - 1) return new { Word = word, Leading = " and " };
            return new { Word = word, Leading = ", " };
        });

    wordsAndSeparators.ToList().ForEach(v => sb.Append(v.Leading).Append(v.Word));
    return sb.ToString();
}

Observe el .ToList () interyectado antes del .ForEach () en la segunda a la última línea.

¿Por qué .ForEach () no está disponible como método de extensión en IEnumerable < T > ? Con un ejemplo como este, parece extraño.

¿Fue útil?

Solución

Porque ForEach (Action) existía antes de que IEnumerable < T > existiera.

Dado que no se agregó con los otros métodos de extensión, uno puede asumir que los diseñadores de C # consideraron que era un mal diseño y prefieren la construcción foreach .


Editar:

Si lo desea, puede crear su propio método de extensión, no anulará el de una List < T > pero funcionará para cualquier otra clase que implemente IEnumerable < T > ; .

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

Otros consejos

Según Eric Lippert, esto es principalmente por razones filosóficas . Deberías leer la publicación completa, pero aquí está la esencia en lo que a mí respecta:

  

Me opongo filosóficamente a   proporcionando tal método, para dos   razones.

     

La primera razón es que al hacerlo   viola la programación funcional   principios que todas las otras secuencias   Los operadores se basan en. Claramente el   Único propósito de una llamada a este método.   es para causar efectos secundarios.

     

El propósito de una expresión es   calcular un valor, no causar un lado   efecto. El propósito de una declaración es   para causar un efecto secundario. El sitio de llamadas   De esta cosa se vería muchísimo   como una expresión (aunque,   Es cierto, ya que el método es   void-return, la expresión podría   solo ser utilizado en una “declaración   expresión "contexto.)

     

No me sienta bien hacer   el único y único operador de secuencia   Eso solo es útil por su lado.   efectos.

     

La segunda razón es que al hacerlo   Añade cero nuevo poder de representación.   al idioma.

Debido a que ForEach () en un IEnumerable es solo una normalidad para cada ciclo como este:

for each T item in MyEnumerable
{
    // Action<T> goes here
}

Solo estoy adivinando aquí, pero poner foreach en IEnumerable haría que los efectos secundarios tuvieran operaciones. Ninguno de los " disponibles " los métodos de extensión causan efectos secundarios, poner un método imperativo como foreach allí enturbiaría la API, supongo. Además, foreach inicializaría la colección perezosa.

Personalmente, he estado defendiendo la tentación de agregar la mía, solo para mantener las funciones sin efectos secundarios separadas de las que tienen efectos secundarios.

ForEach no está en IList, está en la Lista. Estabas usando la Lista concreta en tu ejemplo.

Honestamente, no sé con certeza por qué .ForEach (Acción) no está incluido en IEnumerable pero, correcto, incorrecto o indiferente, así es ...

Sin embargo, sí quería resaltar el problema de rendimiento mencionado en otros comentarios. Hay un impacto de rendimiento basado en cómo se recorre una colección. Es relativamente menor, pero sin embargo, ciertamente existe. Aquí hay un fragmento de código increíblemente rápido y descuidado para mostrar las relaciones ... solo se demora aproximadamente un minuto en ejecutarse.

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Start Loop timing test: loading collection...");
        List<int> l = new List<int>();

        for (long i = 0; i < 60000000; i++)
        {
            l.Add(Convert.ToInt32(i));
        }

        Console.WriteLine("Collection loaded with {0} elements: start timings",l.Count());
        Console.WriteLine("\n<===============================================>\n");
        Console.WriteLine("foreach loop test starting...");

        DateTime start = DateTime.Now;

        //l.ForEach(x => l[x].ToString());

        foreach (int x in l)
            l[x].ToString();

        Console.WriteLine("foreach Loop Time for {0} elements = {1}", l.Count(), DateTime.Now - start);
        Console.WriteLine("\n<===============================================>\n");
        Console.WriteLine("List.ForEach(x => x.action) loop test starting...");

        start = DateTime.Now;

        l.ForEach(x => l[x].ToString());

        Console.WriteLine("List.ForEach(x => x.action) Loop Time for {0} elements = {1}", l.Count(), DateTime.Now - start);
        Console.WriteLine("\n<===============================================>\n");

        Console.WriteLine("for loop test starting...");

        start = DateTime.Now;
        int count = l.Count();
        for (int i = 0; i < count; i++)
        {
            l[i].ToString();
        }

        Console.WriteLine("for Loop Time for {0} elements = {1}", l.Count(), DateTime.Now - start);
        Console.WriteLine("\n<===============================================>\n");

        Console.WriteLine("\n\nPress Enter to continue...");
        Console.ReadLine();
    }

Sin embargo, no te obsesiones demasiado con esto. El rendimiento es la moneda del diseño de la aplicación, pero a menos que su aplicación esté experimentando un impacto de rendimiento real que esté causando problemas de usabilidad, concéntrese en la codificación para el mantenimiento y la reutilización, ya que el tiempo es la moneda de los proyectos empresariales de la vida real ...

ForEach se implementa en la clase concreta List<T>

Solo una suposición, pero la Lista puede iterar sobre sus elementos sin crear un enumerador:

public void ForEach(Action<T> action)
{
    if (action == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
    }
    for (int i = 0; i < this._size; i++)
    {
        action(this._items[i]);
    }
}

Esto puede conducir a un mejor rendimiento. Con IEnumerable, no tiene la opción de usar un bucle for ordinario.

Se llama " Seleccionar " en IEnumerable<T> Estoy iluminado, gracias.

LINQ sigue el modelo de extracción y todos sus métodos (de extensión) deben devolver IEnumerable < T > , excepto por ToList () . El ToList () está ahí para finalizar la cadena de tracción.

ForEach () es del mundo push-model.

Aún puedes escribir tu propio método de extensión para hacer esto, como lo señaló Samuel.

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