Vérifier si la propriété a un attribut

158

Étant donné une propriété dans une classe, avec des attributs - quel est le moyen le plus rapide de déterminer si elle contient un attribut donné? Par exemple:

    [IsNotNullable]
    [IsPK]
    [IsIdentity]
    [SequenceNameAttribute("Id")]
    public Int32 Id
    {
        get
        {
            return _Id;
        }
        set
        {
            _Id = value;
        }
    }

Quelle est la méthode la plus rapide pour déterminer qu'elle possède par exemple l'attribut "IsIdentity"?

Otávio Décio
la source

Réponses:

280

Il n'y a pas de moyen rapide de récupérer les attributs. Mais le code devrait ressembler à ceci (crédit à Aaronaught ):

var t = typeof(YourClass);
var pi = t.GetProperty("Id");
var hasIsIdentity = Attribute.IsDefined(pi, typeof(IsIdentity));

Si vous avez besoin de récupérer les propriétés d'attribut,

var t = typeof(YourClass);
var pi = t.GetProperty("Id");
var attr = (IsIdentity[])pi.GetCustomAttributes(typeof(IsIdentity), false);
if (attr.Length > 0) {
    // Use attr[0], you'll need foreach on attr if MultiUse is true
}
Hans Passant
la source
63
Si vous avez seulement besoin de vérifier l'existence de l'attribut, et de ne récupérer aucune information à partir de celui-ci, l'utilisation Attribute.IsDefinedéliminera une ligne de code et les tableaux / transtypages laids.
Aaronaught le
4
Quelque chose que je viens de rencontrer avec ceci est que certains attributs ont un type différent de leur nom d'attribut. Par exemple "NotMapped" dans System.ComponentModel.DataAnnotations.Schema est utilisé comme [NotMapped]dans la classe mais pour le détecter, vous devez utiliserAttribute.IsDefined(pi, typeof(NotMappedAttribute))
Qjimbo
2
Peut-être plus facile d'utiliser la surcharge générique:IsIdentity[] attr = pi.GetCustomAttributes<IsIdentity>(false);
Mojtaba
@Qjimbo (ou probablement quelqu'un d'autre qui lit) Les attributs sont généralement utilisés sans la partie "Attribut" de leur nom, mais peuvent l'être. Une convention vous permet de l'exclure, donc généralement le type réel a un attribut à la fin de son nom, mais il n'est tout simplement pas utilisé.
Jim Wolff
44

Si vous utilisez .NET 3.5, vous pouvez essayer les arborescences d'expressions. C'est plus sûr que la réflexion:

class CustomAttribute : Attribute { }

class Program
{
    [Custom]
    public int Id { get; set; }

    static void Main()
    {
        Expression<Func<Program, int>> expression = p => p.Id;
        var memberExpression = (MemberExpression)expression.Body;
        bool hasCustomAttribute = memberExpression
            .Member
            .GetCustomAttributes(typeof(CustomAttribute), false).Length > 0;
    }
}
Darin Dimitrov
la source
7
Pour info, une question a été posée sur votre réponse. stackoverflow.com/questions/4158996/…
Greg
12

Vous pouvez utiliser une méthode commune (générique) pour lire un attribut sur un MemberInfo donné

public static bool TryGetAttribute<T>(MemberInfo memberInfo, out T customAttribute) where T: Attribute {
                var attributes = memberInfo.GetCustomAttributes(typeof(T), false).FirstOrDefault();
                if (attributes == null) {
                    customAttribute = null;
                    return false;
                }
                customAttribute = (T)attributes;
                return true;
            }
Manish Basantani
la source
7

Pour mettre à jour et / ou améliorer la réponse de @Hans Passant, je séparerais la récupération de la propriété en une méthode d'extension. Cela a l'avantage supplémentaire de supprimer la chaîne magique désagréable dans la méthode GetProperty ()

public static class PropertyHelper<T>
{
    public static PropertyInfo GetProperty<TValue>(
        Expression<Func<T, TValue>> selector)
    {
        Expression body = selector;
        if (body is LambdaExpression)
        {
            body = ((LambdaExpression)body).Body;
        }
        switch (body.NodeType)
        {
            case ExpressionType.MemberAccess:
                return (PropertyInfo)((MemberExpression)body).Member;
            default:
                throw new InvalidOperationException();
        }
    }
}

Votre test est alors réduit à deux lignes

var property = PropertyHelper<MyClass>.GetProperty(x => x.MyProperty);
Attribute.IsDefined(property, typeof(MyPropertyAttribute));
Seb
la source
7

Si vous essayez de le faire dans une bibliothèque de classes portable PCL (comme moi), voici comment vous pouvez le faire :)

public class Foo
{
   public string A {get;set;}

   [Special]
   public string B {get;set;}   
}

var type = typeof(Foo);

var specialProperties = type.GetRuntimeProperties()
     .Where(pi => pi.PropertyType == typeof (string) 
      && pi.GetCustomAttributes<Special>(true).Any());

Vous pouvez ensuite vérifier le nombre de propriétés qui ont cette propriété spéciale si nécessaire.

A AlTaiar
la source
7

Cela peut maintenant être fait sans arbres d'expression et méthodes d'extension de manière sûre avec la nouvelle fonctionnalité C # nameof()comme celle-ci:

Attribute.IsDefined(typeof(YourClass).GetProperty(nameof(YourClass.Id)), typeof(IsIdentity));

nameof () a été introduit en C # 6

Jim Wolff
la source
6

Vous pouvez utiliser la méthode Attribute.IsDefined

https://msdn.microsoft.com/en-us/library/system.attribute.isdefined(v=vs.110).aspx

if(Attribute.IsDefined(YourProperty,typeof(YourAttribute)))
{
    //Conditional execution...
}

Vous pouvez fournir la propriété que vous recherchez spécifiquement ou vous pouvez les parcourir toutes en utilisant la réflexion, quelque chose comme:

PropertyInfo[] props = typeof(YourClass).GetProperties();
François Musignac
la source
Cela ne compile pas. Vous ne pouvez pas utiliser [] autour de YourProperty ou YourAttribute
lance le
Chaque réponse précédente a utilisé des hypothèses sur les noms de classe, de propriété et d'attribut que j'ai suivies.
Francis Musignac le
Apparaît corrigé maintenant.
lance le
2

C'est une question assez ancienne mais j'ai utilisé

Ma méthode a ce paramètre mais il pourrait être construit:

Expression<Func<TModel, TValue>> expression

Puis dans la méthode ceci:

System.Linq.Expressions.MemberExpression memberExpression 
       = expression.Body as System.Linq.Expressions.MemberExpression;
Boolean hasIdentityAttr = System.Attribute
       .IsDefined(memberExpression.Member, typeof(IsIdentity));
Mark Schultheiss
la source