Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

18 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
martes, 19 de enero de 2021
.NET Core

Leyendo las novedades de C# 9, hay una que pasa casi completamente desapercibida pero que me ha llamado la atención: la posibilidad de convertir prácticamente cualquier tipo en enumerable.

La magia consiste en que, a partir de esta versión, se podrá recorrer con un foreach objetos que, o bien implementen IEnumerable o directamente el conocido GetEnumerator(), como lo hacen los arrays o strings, o bien existe un método extensor con el mismo nombre declarado sobre el tipo.

En el blog de Lukáš Lánský encuentro un ejemplo terrorífico, pero a la vez extremadamente clarificador de la potencia de esta posibilidad:

public static class VonNeumannExtensions
{
    public static IEnumerator<int> GetEnumerator(this int number)
    {
        for (var i = 0; i < number; i++)
        {
            yield return i;	
        }
    }
}

¡Yep! Acabamos de definir el extensor GetEnumerator() sobre el tipo int, lo que quiere decir que ahora podemos recorrerlo con un con  foreach() con total naturalidad:

public class Program
{
    public static void Main()
    {
        foreach (var i in 5) // WTF?
        {
            Console.WriteLine(i);
        }
    }
}

Seguro que podéis imaginar el resultado que se mostrará en consola:

0
1
2
3
4

Visto esto, seguro que podemos imaginar otros usos. Por ejemplo, ¿quién no ha querido alguna vez recorrer un rango de valores de esta forma tan limpia?

foreach (var i in 0..9)
{
    Console.Write(i);
}
// Output: 0123456789

Pues bastaría con crear el correspondiente extensor para el tipo Range:

public static class RangeExtensions
{
    public static IEnumerator<int> GetEnumerator(this Range r)
    {
        for (int i = r.Start.Value; i <= r.End.Value; i++)
        {
            yield return i;
        }
    }
}

Otro ejemplo: imaginad que tenemos una clase Invoice a cuyo código no tenemos acceso, pero deseamos poder recorrer sus líneas de detalle usando foreach. Sería tan sencillo como crear un extensor como el siguiente:

public static class InvoiceExtensions
{
    public static IEnumerator<InvoiceLine> GetEnumerator(this Invoice invoice)
    {
        return invoice.GetLines().GetEnumerator();
    }
}

Y a partir de este momento, ya podríamos hacerlo sin problema:

var invoice = _invoices.GetById(18);
foreach (var line in invoice)
{
    Console.WriteLine($"{line.Description}, {line.Items} items");
}

¿Utilidad real y práctica de esto? Pues creo que escasa para la mayoría de los mortales. Supongo que habrá algunos escenarios en los que el esfuerzo de haber añadido esta característica al compilador merezca la pena, pero en el día a día creo que no es algo que vayamos a tener que utilizar muchas veces.

Si tenéis interés, podéis echar un vistazo a la entrada en Github donde se discute la funcionalidad y se proponen varios escenarios de uso relacionados con tuplas y tipos genéricos.

En cualquier caso, curioso sí que es ;)

Publicado en Variable not found.

Aún no hay comentarios, ¡sé el primero!