2008-11-16 27 views
426

Esto provoca una excepción en tiempo de compilación:¿Por qué C# prohíbe los tipos de atributos genéricos?

public sealed class ValidatesAttribute<T> : Attribute 
{ 

} 

[Validates<string>] 
public static class StringValidation 
{ 

} 

Soy consciente de C# no soporta atributos genéricos. Sin embargo, después de mucho googlear, parece que no puedo encontrar el motivo.

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

+13

Podría hacer [Valida (typeof (cadena)] - Estoy de acuerdo con que los genéricos serían más agradables ... – ConsultUtah

+16

Aunque es una adición muy tardía a esta pregunta, es triste que no solo se atribuyan atributos sino también clases de atributos abstractos (que obviamente no se pueden instanciar como atributos), como esta: 'clase abstracta Base : Atributo {} 'que podría usarse para crear clases derivadas no genéricas como esta:' clase Concreto: Base {} ' – Lucero

+74

Anhelo atributos genéricos y atributos que acepten lambdas. Imagine cosas como '' [DependsOnProperty (f => f.Bar)] '' o '' [ForeignKey (f => f.IdBar)] '' ... –

Respuesta

293

Bueno, no puedo responder por qué no está disponible, pero I puede 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 C anotada de C# 3 debe llegar mañana ... Veré si eso proporciona más información. De todos modos, definitivamente es una decisión de lenguaje en lugar de una de tiempo de ejecución.

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

+3

Me complace escuchar que los atributos genéricos son legales IL. ¡Le agradecería mucho si pudiera preguntar al equipo! Tuve la oportunidad de preguntarle a Anders en PDC y lo olvidé por completo :-) –

+2

He preguntado ... veremos si responden. –

+1

¡Gracias por la información interna! –

68

Un atributo decora una clase en tiempo de compilació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 compilación, debe estar "completo" en el momento de la compilación.

Consulte este MSDN article para obtener más información.

+2

El artículo reafirma que no son posibles, pero sin razón. Comprendo conceptualmente su respuesta. ¿Conoces alguna otra documentación oficial sobre el tema? –

+2

El artículo cubre el hecho de que IL todavía contiene marcadores de posición genéricos que se sustituyen con el tipo real en tiempo de ejecución. El resto fue inferido por mí ... :) – GalacticCowboy

+1

Por lo que vale, VB impone la misma restricción: "Las clases genéricas o contenidas en un tipo genérico no pueden heredar de una clase de atributo". – GalacticCowboy

8

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

En 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 tome en SomeCustomValidationDelegate o ISomeCustomValidator como un parámetro sería un mejor enfoque.

+0

Estoy de acuerdo contigo. He tenido esta pregunta durante mucho tiempo, y actualmente estoy construyendo un sistema de validación. Usé mi terminología actual para hacer la pregunta, pero no tengo la intención de implementar un enfoque basado en este mecanismo. –

+0

Me encontré con esto mientras trabajaba en un diseño para el mismo objetivo: la validación. Intento hacerlo de una forma que sea fácil de analizar de forma automática (es decir, podría generar un informe que describa la validación en la aplicación para confirmación) y que un humano visualice el código. Si no son atributos, no estoy seguro de cuál sería la mejor solución ... Todavía podría probar el diseño del atributo, pero declararé manualmente los atributos específicos del tipo. Es un poco más trabajo, pero el objetivo es la fiabilidad de conocer las reglas de validación (y poder informar sobre ellas para su confirmación). – bambams

+2

se puede comparar con las definiciones de tipo genérico (es decir, typeof (Validates <>)) ... – Yaurthek

15

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

[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 
{ 
    .... 
} 
+3

Desafortunadamente, pierde la escritura en tiempo de compilación cuando consume el atributo. Imagine que el atributo crea algo del tipo genérico. Puedes evitarlo, pero sería bueno; es una de esas cosas intuitivas que te sorprende que no puedas hacer, como la varianza (actualmente). –

+11

Tristemente tratando de no hacer esto es por lo que encontré esta pregunta. Creo que tendré que ocuparme de typeof. Realmente se siente como una palabra clave sucia ahora después de que los genéricos han existido por tanto tiempo. –

+6

También pierde restricciones genéricas ... –

10

Esto no es realmente genérico y todavía tiene que escribir la clase atributo específico según el tipo, pero puede ser capaz de utilizar una interfaz de base genérica para codificar un poco a la defensiva, escribir código menor que requiera de otra manera, obtener beneficios del polimorfismo 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> 
{ 
    T Value { get; } //or whatever that is 
    bool IsValid { get; } //etc 
} 

public class ValidatesStringAttribute : Attribute, ValidatesAttribute<string> 
{ 
    //... 
} 
public class ValidatesIntAttribute : Attribute, ValidatesAttribute<int> 
{ 
    //... 
} 

[ValidatesString] 
public static class StringValidation 
{ 

} 
[ValidatesInt] 
public static class IntValidation 
{ 

} 
1

Mi solución es algo como esto:

public class DistinctType1IdValidation : ValidationAttribute 
{ 
    private readonly DistinctValidator<Type1> validator; 

    public DistinctIdValidation() 
    { 
     validator = new DistinctValidator<Type1>(x=>x.Id); 
    } 

    public override bool IsValid(object value) 
    { 
     return validator.IsValid(value); 
    } 
} 

public class DistinctType2NameValidation : ValidationAttribute 
{ 
    private readonly DistinctValidator<Type2> validator; 

    public DistinctType2NameValidation() 
    { 
     validator = new DistinctValidator<Type2>(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; } 
Cuestiones relacionadas