Pensemos por un momento cómo podemos construir en C# 1.0 o 1.1, una clase contenedora de datos que se pueda utilizar con la construcción foreach para iterar por los elementos que contiene...

Para empezar la clase debe implementar la interface IEnumerable. Esto tampoco supone mucho trabajo ya que, al fin y al cabo, sólo se debe implementar un método, GetEnumerator(). Ahí se empieza complicar la cosa un poco ya que este método devolverá un objeto que implementa la interfaz IEnumerator y que nosotros debemos definir también. La implementación de esta clase es algo más compleja y además debe mantener una referencia al objeto enumerable original.

Ya de por sí, como podemos comprobar, se trata de algo bastante tedioso. Pero es que además de eso en algunos casos es algo muy complicado de hacer. Por ejemplo, si queremos implementar una clase enumerable que contenga los nodos de una jerarquía (un árbol de carpetas, por ejemplo) y que, enumerándolo con foreach, nos devuelva por el orden correcto de visualización todos los elementos del árbol en todos los niveles. Esto ya es algo más complicado y farragoso: hay que meterse en procedimientos recursivos y coordinar las llamadas a Current(), MoveNext() y Reset(). No digo que no se pueda hacer ni mucho menos. Sólo digo que es más complicado todavía.

Los Iteradores de C# 2.0: facilitándonos la vida.

Los nuevos iteradores de C# 2.0 nos permiten crear clases iterables con foreach pero sin apenas esfuerzo y complicación. Además permiten iterar por colecciones de elementos más complejas que una simple lista de elementos, como por ejemplo el árbol jerárquico del que hablaba más atrás.

Imaginemos una clase Contenedor que almacena una lista de objetos de tipo indeterminado de momento (aquí haremos uso de tipos genéricos que comenté en un post hace unos días). La definiremos sin especificar qué tipos de elemento albergará y así la hacemos más útil. Para coseguir que sea enumerable implementará la inerfaz IEnumerable, como antes, pero ahora en lugar de crear un objeto enumerador auxiliar emplearemos una nueva construcción. Veámoslo:

public class Contenedor : IEnumerable
{
    private Tipo[] datos;  //Aquí se almacenan los datos de la clase

    public Add(Tipo dato)... //Habría que implementarla, lo dejo fuera por claridad.

    public IEnumerator GetEnumerator()  //Aquí se implementa la interfaz
    {
        foreach (Tipo dato in datos)
        {
            yield dato;
        }
    }
}

Tal y como se observa en el código anterior, la novedad estriba en el uso de la nueva palabra clave yield. Ésta devuelve el elemento seleccionado y "congela" la ejecución del código (en este caso del bucle que la contiene) hasta que se realiza una nueva llamada al enumerador resultante, momento en el cual el bucle continua. Este es el efecto conseguido, en realidad el compilador hace el trabajo sucio por nosotros ahorrándonos decenas de líneas de código. Muy útil ¿verdad?.

De hecho yield se puede usar de modo más creativo aún para hacer cosas, antes complicadas, de manera sencilla. yield se puede usar con cualquier método que devuelva un IEnumerator (lo normal), o incluso si devuelve un IEnumerable. esto abre nuevas posibiliddes. Por ejemplo, imaginemos que queremos iterar por un subconjunto de datos de una clase contenedora. Podemos conseguirlo devolviendo un IEnumerable añadiéndole este método a la clase anterior:

public IEnumerable<Tipo> SubConjunto(int desde, int hasta)
{
    while (desde < hasta)
    {
        yield datos[desde++];
    }
}

Como vemos lo único que se hace es recorrer los elementos desde el elemento inicial al final devolviéndolos mientras tanto y "deteniendo" la ejecución del bucle con yield hasta la siguiente llamada a la enumeración. Es infinitamente más fácil que antes.

El interesado podrá encontrar un estupendo artículo a fondo sobre este tema en el número 7 de DotnetMania, de Septiembre de 2004. Además de explicarlo a fondo los autores (Miguel Katrib y Mario del Valle), en un alarde de virtuosismo, se curran un equivalente a esta técnica hecha con multi-subproceso, y que funciona en la versión actual de C#. Muy recomendable. El código asociado con esta implementación se puede descargar desde aquí.

💪🏻 ¿Este post te ha ayudado?, ¿has aprendido algo nuevo?
Pues NO te pido que me invites a un café... Te pido algo más fácil y mucho mejor

Escrito por un humano, no por una IA