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, 13 de diciembre de 2022
C#

Otra de las novedades del flamante C# 11 viene a romper una limitación histórica: la ausencia de tipos genéricos en la definición de atributos.

Hasta esta versión del lenguaje, cuando necesitábamos introducir referencias a tipos de datos en un atributo, debíamos pasar obligatoriamente por el uso de un System.Type y el verboso operador typeof(). Además de incómodo, no había forma de limitar los tipos suministrados, dando lugar a errores en tiempo de ejecución que bien podrían haber sido resueltos el compilación con las herramientas apropiadas.

Dicho así quizás sea difícil de entender, pero veámoslo con un ejemplo.

Imaginad el siguiente atributo, donde esperamos como parámetro un tipo de datos que necesitamos que implemente una interfaz llamada IMySerializer:

public class MyAttribute : Attribute
{
    public Type Type { get; set; }
    public MyAttribute(Type type)
    {
        if (!type.IsAssignableTo(typeof(IMySerializer)))
            throw new ArgumentException(nameof(type), "Must implement IMySerializer");
        Type = type;
    }
}

Dada esta definición, podríamos usar el atributo de las siguientes maneras. La primera de ellas generaría un error en tiempo de ejecución, mientras que la segunda se ejecutaría bien:

// Fallará *en ejecución*, pues string no implementa IMySerializer
[MyAttribute(typeof(string))]
public class Friend
{
}

// Funcionará, pues MySerializer implementa IMySerializer
[MyAttribute(typeof(MySerializer))]
public class Friend
{
}

public class MySerializer : IMySerializer { }

Como podéis ver, no hay forma de restringir en tiempo de compilación el tipo de datos que pasamos al atributo [MyAttribute]. Será en ejecución cuando se decida si el tipo suministrado es válido o no, dando lugar a un error en la aplicación.

La idea es que a partir de C# 11 podemos definir el atributo como sigue:

public class MyAttribute<T> : Attribute
       where T : IMySerializer
{
    public Type Type { get;  }
    public MyAttribute()
    {
        Type = typeof(T);
    }
}

Así, el uso de genéricos permite que las restricciones de tipo entren en juego, por lo que el atributo sólo podrá ser usado con tipos que implementen IMySerializer. De esta forma, el equivalente al código anterior daría un error en tiempo de compilación, y no en tiempo de ejecución.

// No compilará, pues string no implementa IMySerializer
[MyAttribute<string>]
public class Friend { }

// Compilará bien
[MyAttribute<MySerializer>]
public class Friend { }

Publicado en Variable not found.

5 Comentarios:

Joseba dijo...

Buenas.

No sé, algo no me acaba de engranar con el ejemplo.

Yo puedo seguir haciendo:

[MyAttribute(typeof(string))]
public class Friend { }


que no petará hasta la ejecución.

Joseba dijo...

Se ha tragado

<MySerializer>

:)

José María Aguilar dijo...

Hola, Joseba!

Puuf, me traicionó un copiar&pegar :(

He actualizado la clase MyAttribute<T<, ahora sí se ve la difencia.

Muchas gracias por comentarlo!!

Joseba dijo...

Zorionak eta Urte Berri On!!!

José María Aguilar dijo...

Felices fiestas igualmente!!