Comment obtenir une liste de propriétés avec un attribut donné?

210

J'ai un type, tet j'aimerais obtenir une liste des propriétés publiques qui ont l'attribut MyAttribute. L'attribut est marqué AllowMultiple = falsecomme suit:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]

Actuellement, ce que j'ai, c'est ceci, mais je pense qu'il y a une meilleure façon:

foreach (PropertyInfo prop in t.GetProperties())
{
    object[] attributes = prop.GetCustomAttributes(typeof(MyAttribute), true);
    if (attributes.Length == 1)
    {
         //Property with my custom attribute
    }
}

Comment puis-je améliorer cela? Mes excuses s'il s'agit d'un doublon, il y a une tonne de fils de réflexion là-bas ... semble être un sujet assez brûlant.

wsanville
la source
Nan. Vous avez besoin d'un PropertyInfo avant de savoir si la propriété a un attribut.
Hans Passant

Réponses:

391
var props = t.GetProperties().Where(
                prop => Attribute.IsDefined(prop, typeof(MyAttribute)));

Cela évite d'avoir à matérialiser des instances d'attribut (c'est-à-dire qu'il est moins cher que GetCustomAttribute[s]().

Marc Gravell
la source
1
Bonne suggestion. J'aurai cependant besoin de l'instance d'attribut, mais j'aime ça.
wsanville
1
Je cherchais simplement un moyen de vérifier l'existence d'un attribut sans l'effet secondaire que la propriété obtient est appelée. Merci Marc, ça marche!
Örjan Jämte
1
@ ÖrjanJämte la propriété getn'est pas appelée même lors de l'utilisation GetCustomAttributes; cependant, l'attribut est instancié , ce qui n'est pas gratuit. Si vous n'avez pas besoin de vérifier des valeurs spécifiques de l'attribut, IsDefinedc'est moins cher. Et en 4.5, il existe des moyens de vérifier les données d'instanciation sans créer réellement d'instances d'attribut (bien que cela soit destiné à des scénarios très spécifiques uniquement)
Marc Gravell
2
@bjhuffine msdn.microsoft.com/en-us/library/…
Marc Gravell
2
pour le noyau dotnet: var props = t.GetProperties (). Where (e => e.IsDefined (typeof (MyAttribute)));
Rtype
45

La solution que j'utilise le plus est basée sur la réponse de Tomas Petricek. Je veux habituellement faire quelque chose avec à la fois l'attribut et la propriété.

var props = from p in this.GetType().GetProperties()
            let attr = p.GetCustomAttributes(typeof(MyAttribute), true)
            where attr.Length == 1
            select new { Property = p, Attribute = attr.First() as MyAttribute};
wsanville
la source
+1 - "Je veux généralement faire quelque chose à la fois avec l'attribut et la propriété", c'est ce que je cherchais - merci beaucoup d'avoir posté votre réponse!
Yawar Murtaza
34

Pour autant que je sache, il n'y a pas de meilleure façon de travailler avec la bibliothèque Reflection de manière plus intelligente. Cependant, vous pouvez utiliser LINQ pour rendre le code un peu plus agréable:

var props = from p in t.GetProperties()
            let attrs = p.GetCustomAttributes(typeof(MyAttribute), true)
            where attrs.Length != 0 select p;

// Do something with the properties in 'props'

Je crois que cela vous aide à structurer le code d'une manière plus lisible.

Tomas Petricek
la source
13

Il y a toujours LINQ:

t.GetProperties().Where(
    p=>p.GetCustomAttributes(typeof(MyAttribute), true).Length != 0)
P papa
la source
6

Si vous traitez régulièrement avec Attributes in Reflection, il est très, très pratique de définir certaines méthodes d'extension. Vous le constaterez dans de nombreux projets. Celui-ci en est un que j'ai souvent:

public static bool HasAttribute<T>(this ICustomAttributeProvider provider) where T : Attribute
{
  var atts = provider.GetCustomAttributes(typeof(T), true);
  return atts.Length > 0;
}

que vous pouvez utiliser comme typeof(Foo).HasAttribute<BarAttribute>();

D'autres projets (par exemple StructureMap) ont des classes ReflectionHelper à part entière qui utilisent des arbres d'expression pour avoir une syntaxe fine pour identifier par exemple PropertyInfos. L'utilisation ressemble alors à ça:

ReflectionHelper.GetProperty<Foo>(x => x.MyProperty).HasAttribute<BarAttribute>()
flq
la source
2

En plus des réponses précédentes: il est préférable d'utiliser la méthode Any()plutôt que de vérifier la longueur de la collection:

propertiesWithMyAttribute = type.GetProperties()
  .Where(x => x.GetCustomAttributes(typeof(MyAttribute), true).Any());

L'exemple sur dotnetfiddle: https://dotnetfiddle.net/96mKep

payeur
la source
@ cogumel0 Tout d'abord, bien sûr, .Any()ne vérifie pas la longueur. Mais ma réponse ne concernait pas les propriétés trouvées avec exactement un attribut. Deuxièmement, je ne suis pas sûr que vous ayez lu le code correctement - .Anyméthode appelée sur le résultat de la GetCustomAttrubutesméthode. Le type de propertiesWithMyAttributesera donc la collection des propriétés. Consultez l'exemple sur dotnetfiddle (j'ajoute le lien vers la réponse).
2018
1
Vous pouvez remplacer .Where par .Any, car .Any autorise également les lambdas.
PRMan