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


Esto causa 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 soporta atributos genéricos. Sin embargo, después de mucho Googlear, no puedo encontrar la razón.

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

Author: Deduplicator, 2008-11-16

6 answers

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 usas IL directamente puedes crear un atributo genérico. La parte de la especificación de C# 3 que la prohíbe-sección 10.1.4 "Especificación base de clase" no da ninguna justificación.

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

Mi una copia de la especificación anotada de C# 3 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.

EDIT: Respuesta de Eric Lippert (parafraseada): 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.

 314
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-11-18 15:46:27

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. Dado que el atributo puede afectar a la compilación, tiene que estar "completo" en tiempo de compilación.

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

 73
Author: GalacticCowboy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-11-16 19:09:59

No se 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
{
    ....
}
 17
Author: GeekyMonkey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-11-16 21:45:03

Esto no es realmente genérico y todavía tiene que escribir una clase de atributo específica por tipo, pero es posible que pueda usar una interfaz base genérica para codificar un poco a la defensiva, escribir menos código del requerido, obtener beneficios de 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
{

}
 11
Author: nawfal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-07 06:45:46

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 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 dependiendo del tipo, un atributo puede no ser el mejor enfoque.

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

 8
Author: ichiban,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-11-16 19:15:04

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; }
 1
Author: razon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-29 06:56:01