Question Pourquoi C # interdit-il les types d'attributs génériques?


Cela provoque une exception à la compilation:

public sealed class ValidatesAttribute<T> : Attribute
{

}

[Validates<string>]
public static class StringValidation
{

}

Je réalise que C # ne supporte pas les attributs génériques. Cependant, après beaucoup de recherches sur Google, je n'arrive pas à trouver la raison.

Est-ce que quelqu'un sait pourquoi les types génériques ne peuvent pas dériver de Attribute? Des théories?


446
2017-11-16 18:48


origine


Réponses:


Eh bien, je ne peux pas répondre pourquoi il n'est pas disponible, mais je pouvez confirmez que ce n'est pas un problème CLI. La spécification CLI ne le mentionne pas (pour autant que je sache) et si vous utilisez directement IL, vous pouvez créer un attribut générique. La partie de la spécification C # 3 qui l'interdit - section 10.1.4 "Spécification de la classe" ne donne aucune justification.

La spécification annotée ECMA C # 2 ne fournit pas non plus d'informations utiles, bien qu'elle fournisse un exemple de ce qui n'est pas autorisé.

Ma copie de la spécification C # 3 annotée devrait arriver demain ... Je verrai si cela donne plus d'informations. Quoi qu’il en soit, c’est définitivement une décision linguistique plutôt qu’une exécution.

EDIT: Réponse d'Eric Lippert (paraphrasée): aucune raison particulière, sauf pour éviter la complexité dans le langage et le compilateur pour un cas d'utilisation qui n'ajoute pas beaucoup de valeur.


310
2017-11-16 19:25



Un attribut décore une classe lors de la compilation, mais une classe générique ne reçoit pas ses informations de type final avant l'exécution. Étant donné que l'attribut peut affecter la compilation, il doit être "complet" au moment de la compilation.

Regarde ça Article MSDN pour plus d'informations.


69
2017-11-16 19:09



Je ne sais pas pourquoi ce n'est pas autorisé, mais c'est une solution possible

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

15
2017-11-16 21:45



Ce n'est pas vraiment générique et vous devez toujours écrire une classe d'attributs spécifique par type, mais vous pouvez utiliser une interface de base générique pour coder un peu défensivement, écrire un code plus petit que nécessaire, bénéficier des avantages du polymorphisme, 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
{

}

10
2017-11-07 06:45



C'est une très bonne question. D'après mon expérience avec les attributs, je pense que la contrainte est en place car, lors de la réflexion sur un attribut, cela créerait une condition dans laquelle vous devriez vérifier toutes les permutations de types possibles: typeof(Validates<string>), typeof(Validates<SomeCustomType>), etc...

À mon avis, si une validation personnalisée est requise en fonction du type, un attribut peut ne pas être la meilleure approche.

Peut-être une classe de validation qui prend en compte SomeCustomValidationDelegate ou un ISomeCustomValidator en tant que paramètre serait une meilleure approche.


8
2017-11-16 19:15



Ma solution de contournement est quelque chose comme ceci:

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
2018-01-29 06:56