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:
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.
Se ha tragado
<MySerializer>
:)
Hola, Joseba!
Puuf, me traicionó un copiar&pegar :(
He actualizado la clase MyAttribute<T<, ahora sí se ve la difencia.
Muchas gracias por comentarlo!!
Zorionak eta Urte Berri On!!!
Felices fiestas igualmente!!
Enviar un nuevo comentario