Observaciones con respecto a un método extensor ForEach<T>

Recientemente, Valeriano Tórtola publicó un excelente post en el que mostraba la utilización de un método extensor para IEnumerable<T> llamado ForEach<T> para aplicar una misma modificación a todos los elementos de una secuencia obtenida como resultado de una consulta de LINQ to DataSets.


El método extensor en cuestión proviene del blog de Glenn Brock, Program Manager en Microsoft, y su código fuente es el siguiente:


   public static class IEnumerableUtils
   {
      public static void ExtForEach<T>(this IEnumerable<T> collection, Action<T> action)
      {
         foreach(T item in collection)
            action(item);
      }
   }


Lo primero que me llamó la atención al ver el método es que no es “encadenable”: siendo void su tipo de retorno, deberá ser usado obligatoriamente al final de la “cadena alimenticia” de una expresión de consulta, como ha hecho Valeriano en su ejemplo. Pero en el fondo, no hay nada malo en esto: otros métodos extensores como Count o Sum tampoco son encadenables. Y en el fondo, el objetivo de este método es hacer algo “procedimental” sobre cada elemento de la secuencia de entrada; para todo lo demás, ya tenemos a MasterCardSelect :-). Observe además que el método no es lazy (de ejecución on demand): se “traga” toda la secuencia de entrada ipso facto. Pero tampoco podría serlo, si no devuelve nada.


He renombrado el método a ExtForEach para evitar colisiones con el método ForEach de las listas genéricas que usaré en los ejemplos. Y éste es el primer posible señalamiento a hacerle al método: su nombre. Se producirá una colisión de nombres al aplicar ForEach directamente (sin ninguna otra llamada por medio) a listas genéricas, que ya tienen un ForEach, y en ella saldrá perdiendo el método extensor, que tiene la menor prioridad. Aunque podría aducirse que ese comportamiento es precisamente el adecuado, ello podría ser fuente de confusión.


Pero lo que creo que menos me gusta de este método extensor es que puede producir la falsa sensación de que puede ser utilizado de manera universal para realizar modificaciones cualesquiera sobre los elementos de una secuencia, sin que el compilador pueda ser de ninguna ayuda al respecto. Más de un principiante será “mordido” por esto:


   List<string> strList = new List<string> { “ana”, “barbara”, “celia”, “diana” };
   strList.ExtForEach( s => { s = s.ToUpper(); });
   // parafraseando a JI, “la lista sigue igual”
   strList.ExtForEach(s => { Console.WriteLine(s); });


Al menos, cuando se intenta esto:


   foreach (string s in strList)
      s = s.ToUpper();


el compilador advierte correctamente de que no se puede asignar a la variable de iteración del bucle.


Tal vez lo que esté yo echando en falta aquí es el modificador const de los métodos en C++ (oh no, Rafa, not again! :-).


En resumen, que si yo estuviera en situación de tomar esa decisión, creo que NO agregaría este método a la librería de .NET. Si este ExtForEach o como se llame siempre va a estar al final de la cadena de consulta y va a recorrer toda la secuencia de entrada, pues que los programadores utilicen la construcción foreach del lenguaje cuando necesiten hacer algo así. El ejemplo de Valeriano quedaría como sigue:


foreach(var EmployeeRow in EmployeesTable.
      Where(
         EmployeeRow => !EmployeeRow.IsNull(“Country”) &&
         EmployeeRow.Field<String>(“Country”) == “UK” &&
         !EmployeeRow.IsNull(“FirstName”) &&
         EmployeeRow.Field<String>(“FirstName”) != String.Empty))
   EmployeeRow.SetField<String>(“Initial”,
      EmployeeRow.Field<String>(“FirstName”).Substring(0, 1));


Se parece bastante, ¿no?

Octavio Hernandez

Desarrollador y consultor en tecnologías .NET. Microsoft C# MVP entre 2004 y 2010.

3 comentarios en “Observaciones con respecto a un método extensor ForEach<T>

  1. Hola, Tommy!

    Efectivamente, porque la función lambda recibe cada uno de los elementos de la lista por valor. En el caso de los tipos valor, será una copia del dato – modificarla no alterará el dato original.

    Pero en el caso de los tipos referencia, se trabajará sobre una copia de la referencia; eso nos permitirá modificar el objeto referenciado (siempre que no sea inmutable, como string o un tipo anónimo), pero no alterar las referencias originales en sí.

    Salu2 – Octavio

  2. Hola Octavio, gran post!!

    Al principio me gusto mucho el método en cuestión por lo compacto que quedaba, pero no te falta nada de razón en lo que comentas, de hecho para evitar confundir a los programadores juniors esta mañana cambiamos la firma del metodo extensor y restringimos el tipo al de nuestras entidades, para que no pasen cosas similares a las que comentas 😀

    Un saludo.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *