¿Por qué C # prohíbe los tipos de atributos generics?

Esto provoca una excepción en tiempo de comstackción:

public sealed class ValidatesAttribute : Attribute { } [Validates] public static class StringValidation { } 

Me doy cuenta de que C # no admite atributos generics. Sin embargo, después de mucho googlear, parece que no puedo encontrar el motivo.

¿Alguien sabe por qué los tipos generics no pueden derivarse de Attribute ? ¿Alguna teoria?

Bueno, no puedo responder por qué no está disponible, pero puedo confirmar que no es un problema de CLI. La especificación CLI no lo menciona (hasta donde puedo ver) y si usa IL directamente puede crear un atributo genérico. La parte de la especificación de C # 3 que lo prohíbe – la sección 10.1.4 “Especificación de base de clases” no da ninguna justificación.

La especificación anotada de ECMA C # 2 tampoco proporciona ninguna información útil, aunque proporciona un ejemplo de lo que no está permitido.

Mi copia de la especificación anotada de C # 3 debe llegar mañana … Veré si eso da más información. De todos modos, definitivamente es una decisión de lenguaje en lugar de una de tiempo de ejecución.

EDITAR: Respuesta de Eric Lippert (parafraseado): no hay una razón en particular, excepto para evitar la complejidad tanto en el lenguaje como en el comstackdor para un caso de uso que no agrega mucho valor.

Un atributo decora una clase en tiempo de comstackción, pero una clase genérica no recibe su información de tipo final hasta el tiempo de ejecución. Como el atributo puede afectar la comstackción, debe estar “completo” en el momento de la comstackción.

Vea este artículo de MSDN para más información.

No sé por qué no está permitido, pero esta es una posible solución

 [AttributeUsage(AttributeTargets.Class)] public class ClassDescriptionAttribute : Attribute { public ClassDescriptionAttribute(Type KeyDataType) { _KeyDataType = KeyDataType; } public Type KeyDataType { get { return _KeyDataType; } } private Type _KeyDataType; } [ClassDescriptionAttribute(typeof(string))] class Program { .... } 

Esto no es realmente genérico y usted todavía tiene que escribir clases de atributos específicos por tipo, pero puede usar una interfaz base genérica para codificar un poco a la defensiva, escribir un código menor de lo requerido, obtener beneficios del polymorphism, etc.

 //an interface which means it can't have its own implementation. //You might need to use extension methods on this interface for that. public interface ValidatesAttribute { T Value { get; } //or whatever that is bool IsValid { get; } //etc } public class ValidatesStringAttribute : Attribute, ValidatesAttribute { //... } public class ValidatesIntAttribute : Attribute, ValidatesAttribute { //... } [ValidatesString] public static class StringValidation { } [ValidatesInt] public static class IntValidation { } 

Esta es una muy buena pregunta. En mi experiencia con los atributos, creo que la restricción está en su lugar porque al reflexionar sobre un atributo crearía una condición en la que debería verificar todas las posibles permutaciones de tipo: typeof(Validates) , typeof(Validates) , etc …

En mi opinión, si se requiere una validación personalizada según el tipo, un atributo puede no ser el mejor enfoque.

Quizás una clase de validación que SomeCustomValidationDelegate un SomeCustomValidationDelegate o un ISomeCustomValidator como parámetro sería un mejor enfoque.

Mi solución es algo como esto:

 public class DistinctType1IdValidation : ValidationAttribute { private readonly DistinctValidator validator; public DistinctIdValidation() { validator = new DistinctValidator(x=>x.Id); } public override bool IsValid(object value) { return validator.IsValid(value); } } public class DistinctType2NameValidation : ValidationAttribute { private readonly DistinctValidator validator; public DistinctType2NameValidation() { validator = new DistinctValidator(x=>x.Name); } public override bool IsValid(object value) { return validator.IsValid(value); } } ... [DataMember, DistinctType1IdValidation ] public Type1[] Items { get; set; } [DataMember, DistinctType2NameValidation ] public Type2[] Items { get; set; }