Cela provoque une exception lors de 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?
Eh bien, je ne peux pas dire pourquoi ce n’est pas disponible, mais je peux confirme que ce n’est pas un problème de la CLI. La spécification CLI ne le mentionne pas (autant que je puisse voir) 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 - la section 10.1.4 "Spécification de base de classe" ne fournit aucune justification.
La spécification annotée ECMA C # 2 ne donne aucune information utile non plus, bien qu'elle donne un exemple de ce qui n'est pas autorisé.
Ma copie de la spécification annotée en C # 3 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 que d’exécution.
EDIT: Réponse d'Eric Lippert (paraphrasé): aucune raison particulière, sauf pour éviter la complexité du langage et du compilateur pour un cas d'utilisation qui n'ajoute pas grand chose.
Un attribut décore une classe au moment de la compilation, mais une classe générique ne reçoit ses informations de type final qu'au moment de l'exécution. Puisque l'attribut peut affecter la compilation, il doit être "complet" au moment de la compilation.
Consultez cet article article MSDN pour plus d'informations.
Je ne sais pas pourquoi ce n'est pas permis, mais c'est une solution de contournement 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
{
....
}
Ce n'est pas vraiment générique et vous devez toujours écrire une classe d'attribut spécifique par type, mais vous pourrez peut-être utiliser une interface de base générique pour coder un peu de manière défensive, écrire du code moins important que nécessaire, tirer parti 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
{
}
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 si vous réfléchissiez sur un attribut, cela créerait une condition dans laquelle vous auriez à vérifier toutes les permutations de type 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 qu'une classe de validation qui prend comme paramètre SomeCustomValidationDelegate
ou ISomeCustomValidator
constituerait une meilleure approche.
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; }
Ce n'est pas actuellement une fonctionnalité du langage C #, cependant il y a beaucoup de discussions sur le repo officiel du langage C # .
De quelques notes de réunion :
Même si cela fonctionnerait en principe, il y a des bugs dans la plupart des versions du runtime pour qu’il ne fonctionne pas correctement (c’était jamais exercé).
Nous avons besoin d'un mécanisme pour comprendre sur quelle exécution cible il fonctionne. Nous besoin de cela pour beaucoup de choses, et sont en train de regarder cela. Jusqu'à ce que alors, nous ne pouvons pas le prendre.
Candidat pour une version majeure en C #, si nous pouvons en faire un nombre suffisant des versions d'exécution traitent avec elle.