How to: Obtener controles de un formulario con generics II (Linq al rescate).

Ayer Lluis escribía este gran post: How to: Obtener controles de un formulario con generics. Como bien dice es una pregunta… recurrente en todos los sitios 🙂

Lo bueno de eso del desarrollo es que para todo hay varias soluciones, así que aquí os propongo otra, pero usando Linq. Personalmente me encanta Linq, supongo que es porqué siempre me han fascinado los lenguajes funcionales…

Antes que nada tenemos que solucionar un temilla: Linq funciona sobre IEnumerable<T> pero la propiedad Controls de un Control devuelve un objeto de tipo ControlCollection (otra de esas n-mil clases que no tienen sentido alguno y que existen sólo porque no teníamos generics en la versión 1 del framework). Así que el primer paso es obtener un IEnumerable<Control> a partir de una ControlCollection. Con un método extensor eso es trivial:

public static IEnumerable<Control> AsEnumerable (this Control.ControlCollection @this)
{
foreach (var control in @this) yield return (Control)control;
}

Ale… listos, con eso transformamos la CollectionControl en un IEnumerable<Control> y tenemos acceso a todo el potencial de Linq… Y como queda el método para obtener todos los controles de un tipo? Pues así:

public static IEnumerable<T> GetAllControls<T>(this Control @this) where T : Control
{
return @this.Controls.AsEnumerable().Where(x => x.GetType() == typeof(T)).
Select(y=>(T)y).
Union(@this.Controls.AsEnumerable().SelectMany(x => GetAllControls<T>(x)).
Select(y=>(T)y));
}

¡No me diréis que no es precioso: no hay bucles, no hay ifs… Os he dicho que me encanta Linq? 😉

Hay una pequeña diferencia entre la versión de Lluís y esta mía: la versión de Lluís usa una List<Control> en la que añade todas las referencias (copia las referencias a la lista. Ojo: las referencias, no los controles). Esa versión que usa Linq, no copia las referencias en ningún sitio, sinó que simplemente itera sobre la coleccion original: no crea listas internas, ni nada… Ese es el poder de Linq!

Es cierto que al usar IEnumerable<T> como valor de retorno, perdemos el método ForEach() (puesto que IEnumerable<T> no lo tiene y Linq no lo proporciona), pero hacer un método ForEach es trivial:

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

De todos modos, si os preguntáis porque Linq no ofrece un método ForEach quizá deberíais leeros este post de Octavio: Observaciones con respecto a un método extensor ForEach<T>.

Un  saludo a todos y gracias a Lluís por darme “la excusa” de hacer otro post! 😉

3 comentarios sobre “How to: Obtener controles de un formulario con generics II (Linq al rescate).”

  1. 🙂
    Precioso! Te ha quedado muy gonito, fucking master!

    Y aunque al usar IEnumerable pierdas el ForEach, siempre queda el recurso (aunque menos elegante) de hacer:

    this.GetAllControls().ToList().ForEach(p => ApplyFormat(p));

    Saludod,

Deja un comentario

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