Pregunta

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

public sealed class ValidatesAttribute<T> : Attribute
{

}

[Validates<string>]
public static class StringValidation
{

}

Me doy cuenta de que C # no admite atributos genéricos. Sin embargo, después de mucho buscar en Google, parece que no puedo encontrar la razón.

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

¿Fue útil?

Solución

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 (por lo que puedo ver) y si usa IL directamente, puede crear un atributo genérico. La parte de la especificación C # 3 que lo prohíbe - sección 10.1.4 & Quot; Especificación base de clase & Quot; no da ninguna justificación.

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

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

EDITAR: Respuesta de Eric Lippert (parafraseado): ninguna 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.

Otros consejos

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 ser & Quot; complete & Quot; en tiempo de compilación.

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

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

[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 aún tiene que escribir una clase de atributo específica por tipo, pero puede usar una interfaz base genérica para codificar un poco a la defensiva, escribir un código menor que el requerido, 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
{

}

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 tendría que verificar todas las permutaciones de tipo posibles: typeof(Validates<string>), typeof(Validates<SomeCustomType>), 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 tome 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<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; }

Esta no es actualmente una característica del lenguaje C #, sin embargo se discute mucho sobre el lenguaje oficial C # repo .

De algunas notas de la reunión :

  

Aunque esto funcionaría en principio, hay errores en la mayoría   versiones del tiempo de ejecución para que no funcione correctamente (era   nunca ejercido).

     

Necesitamos un mecanismo para entender en qué tiempo de ejecución de destino funciona. Nosotros   lo necesito para muchas cosas, y actualmente lo estamos viendo. Hasta   entonces, no podemos tomarlo.

     

Candidato para una versión principal de C #, si podemos hacer un número suficiente   de versiones de tiempo de ejecución se ocupan de ello.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top