Comment créer des attributs autorisés en double

96

J'utilise un attribut personnalisé hérité d'une classe d'attributs. Je l'utilise comme ceci:

[MyCustomAttribute("CONTROL")]
[MyCustomAttribute("ALT")]
[MyCustomAttribute("SHIFT")]
[MyCustomAttribute("D")]
public void setColor()
{

}

Mais l'erreur "Dupliquer l'attribut 'MyCustomAttribute'" s'affiche.
Comment puis-je créer un attribut autorisé en double?

ebattulga
la source

Réponses:

184

Collez un AttributeUsageattribut sur votre classe d'attribut (oui, c'est une bouchée) et définissez-le AllowMultiplesur true:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public sealed class MyCustomAttribute: Attribute
Anton Gogolev
la source
6
Juste curieux - pourquoi une classe «scellée»?
Tomas Aschan
18
Microsoft recommande de sceller les classes d'attributs dans la mesure du possible: msdn.microsoft.com/en-us/library/2ab31zeh.aspx
Anton Gogolev
3
Pourquoi scellé? En bref: accélère la recherche d'attributs et n'a aucun autre impact.
Noel Widmer
Sauf que cela empêche quiconque de réutiliser votre code. A noter que les attributs de validation dans DataAnnotations ne sont pas scellés, ce qui est extrêmement utile car cela permet d'en créer des spécialisations.
Neutrino
@Neutrino scellé doit être utilisé chaque fois que vous ne vous attendez pas à ce que vos classes soient héritées ou que vous ne concevez pas. De plus, lorsque l'héritage peut devenir la source de bogues ex: implémentations thread-safe.
Francisco Neto
20

AttributeUsageAttribute ;-p

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class MyAttribute : Attribute
{}

Notez, cependant, que si vous utilisez ComponentModel ( TypeDescriptor), il ne prend en charge qu'une seule instance d'attribut (par type d'attribut) par membre; la réflexion brute prend en charge n'importe quel nombre ...

Marc Gravell
la source
13

La solution d'Anton est correcte, mais il y a un autre piège .

En bref, à moins que votre attrbiute personnalisé ne remplace TypeId, y accéder via PropertyDescriptor.GetCustomAttributes()ne renverra qu'une seule instance de votre attribut.

mcdrewski
la source
Mais cela fonctionne via: var customAtt = propertyInfo.GetCustomAttributes <MyCustomAttribute> ();
oo_dev
8

Par défaut, les Attributes sont limités à être appliqués une seule fois à un seul champ / propriété / etc. Vous pouvez le voir à partir de la définition de la Attributeclasse sur MSDN :

[AttributeUsageAttribute(..., AllowMultiple = false)]
public abstract class Attribute : _Attribute

Par conséquent, comme d'autres l'ont noté, toutes les sous-classes sont limitées de la même manière, et si vous avez besoin de plusieurs instances du même attribut, vous devez définir explicitement AllowMultiplesur true:

[AttributeUsage(..., AllowMultiple = true)]
public class MyCustomAttribute : Attribute

Sur les attributs qui autorisent plusieurs utilisations, vous devez également remplacer la TypeIdpropriété pour vous assurer que des propriétés telles que PropertyDescriptor.Attributes fonctionnent comme prévu. Le moyen le plus simple de le faire est d'implémenter cette propriété pour renvoyer l'instance d'attribut elle-même:

[AttributeUsage(..., AllowMultiple = true)]
public class MyCustomAttribute : Attribute
{
    public override object TypeId
    {
        get
        {
            return this;
        }
    }
}

(Publier cette réponse non pas parce que les autres ont tort, mais parce que c'est une réponse plus complète / canonique.)

Ian Kemp
la source
3

Comme alternative, pensez à repenser votre attribut pour permettre une séquence.

[MyCustomAttribute(Sequence="CONTROL,ALT,SHIFT,D")]

ou

[MyCustomAttribute("CONTROL-ALT-SHIFT-D")]

puis analysez les valeurs pour configurer votre attribut.

Pour un exemple de cela, consultez le code source AuthorizeAttribute dans ASP.NET MVC à l' adresse www.codeplex.com/aspnet .

Tvanfosson
la source
3
Il est même possible que le MyCustomAttributeconstructeur prenne un tableau de chaînes, a string[], avec ou sans le paramsmodificateur. Ensuite, il pourrait être appliqué avec la syntaxe [MyCustom("CONTROL", "ALT", "SHIFT", "D")](avec params).
Jeppe Stig Nielsen
2

Une fois que vous avez ajouté AttributeUsage, assurez-vous d'ajouter cette propriété à votre classe d'attribut

public override object TypeId
{
  get
  {
    return this;
  }
}
Essieu
la source