Nota: Este post ha sido importado de mi blog de geeks.ms. Es posible que algo no se vea del todo "correctamente". En cualquier caso puedes acceder a la versión original aquí
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
public static IEnumerable<Control> AsEnumerable (this Control.ControlCollection @this)
{
foreach (var control in @this) yield return (Control)control;
}
<p>
Ale… listos, con eso <em>transformamos</em> 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í:
</p>
<div style="border-bottom: silver 1px solid; text-align: left; border-left: silver 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: 'Courier New', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow: auto; border-top: silver 1px solid; cursor: text; border-right: silver 1px solid; padding-top: 4px" id="codeSnippetWrapper">
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> IEnumerable<T> GetAllControls<T>(<span style="color: #0000ff">this</span> Control @<span style="color: #0000ff">this</span>) <span style="color: #0000ff">where</span> T : Control<br />{<br /> <span style="color: #0000ff">return</span> @<span style="color: #0000ff">this</span>.Controls.AsEnumerable().Where(x => x.GetType() == <span style="color: #0000ff">typeof</span>(T)).<br /> Select(y=>(T)y).<br /> Union(@<span style="color: #0000ff">this</span>.Controls.AsEnumerable().SelectMany(x => GetAllControls<T>(x)).<br /> Select(y=>(T)y));<br />}</pre>
<p>
</div>
<p>
¡No me diréis que no es precioso: no hay bucles, no hay ifs… Os he dicho que me encanta Linq? 😉
</p>
<p>
Hay una <em>pequeña</em> 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 <em>simplemente</em> itera sobre la coleccion original: no crea listas internas, ni nada… Ese es el poder de Linq!
</p>
<p>
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:
</p>
<div style="border-bottom: silver 1px solid; text-align: left; border-left: silver 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: 'Courier New', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow: auto; border-top: silver 1px solid; cursor: text; border-right: silver 1px solid; padding-top: 4px" id="codeSnippetWrapper">
<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">void</span> ForEach<T>(<span style="color: #0000ff">this</span> IEnumerable<T> @<span style="color: #0000ff">this</span>, Action<T> action)<br />{<br /> <span style="color: #0000ff">foreach</span> (T t <span style="color: #0000ff">in</span> @<span style="color: #0000ff">this</span>)<br /> {<br /> action(t);<br /> }<br />}</pre>
<p>
</div>
<p>
De todos modos, si os preguntáis <strong>porque Linq no ofrece un método ForEach</strong> quizá deberíais leeros este post de Octavio: <a href="http://geeks.ms/blogs/ohernandez/archive/2008/09/13/observaciones-con-respecto-al-m-233-todo-extensor-foreach-lt-t-gt.aspx">Observaciones con respecto a un método extensor ForEach<T></a>.
</p>
<p>
Un  saludo a todos y gracias a Lluís por darme “la excusa” de hacer otro post! 😉
</p>