web-dev-qa-db-fra.com

Réflexion - Obtenir le nom et la valeur de l'attribut sur la propriété

J'ai une classe, appelons-le Book avec une propriété appelée Name. Avec cette propriété, j'ai un attribut associé.

public class Book
{
    [Author("AuthorName")]
    public string Name
    {
        get; private set; 
    }
}

Dans ma méthode principale, j'utilise la réflexion et souhaite obtenir une paire clé-valeur de chaque attribut pour chaque propriété. Ainsi, dans cet exemple, je m'attendrais à voir "Auteur" pour le nom de l'attribut et "Nom de l'auteur" pour la valeur de l'attribut.

Question: Comment puis-je obtenir le nom et la valeur de l'attribut sur mes propriétés à l'aide de Reflection?

189
developerdoug

Utilisez typeof(Book).GetProperties() pour obtenir un tableau d'instances PropertyInfo. Utilisez ensuite GetCustomAttribute() sur chaque PropertyInfo pour voir si l’un d’eux a le type d’attribut Author. S'ils le font, vous pouvez obtenir le nom de la propriété à partir des informations de la propriété et les valeurs d'attribut à partir de l'attribut.

Quelque chose dans ce sens pour rechercher dans un type les propriétés ayant un type d'attribut spécifique et pour renvoyer les données dans un dictionnaire (notez que cela peut être rendu plus dynamique en passant des types à la routine):

public static Dictionary<string, string> GetAuthors()
{
    Dictionary<string, string> _dict = new Dictionary<string, string>();

    PropertyInfo[] props = typeof(Book).GetProperties();
    foreach (PropertyInfo prop in props)
    {
        object[] attrs = prop.GetCustomAttributes(true);
        foreach (object attr in attrs)
        {
            AuthorAttribute authAttr = attr as AuthorAttribute;
            if (authAttr != null)
            {
                string propName = prop.Name;
                string auth = authAttr.Name;

                _dict.Add(propName, auth);
            }
        }
    }

    return _dict;
}
232
Adam Markowitz

Pour obtenir tous les attributs d'une propriété dans un dictionnaire, utilisez ceci:

typeof(Book)
  .GetProperty("Name")
  .GetCustomAttributes(false)
  .ToDictionary(a => a.GetType().Name, a => a);

souvenez-vous de passer de false à true si vous souhaitez également inclure des attributs hérités.

90
Mo Valipour

Si vous souhaitez uniquement une valeur d'attribut spécifique, par exemple Display Attribute, vous pouvez utiliser le code suivant:

var pInfo = typeof(Book).GetProperty("Name")
                             .GetCustomAttribute<DisplayAttribute>();
var name = pInfo.Name;
35
maxspan

Vous pouvez utiliser GetCustomAttributesData() et GetCustomAttributes() :

var attributeData = typeof(Book).GetProperty("Name").GetCustomAttributesData();
var attributes = typeof(Book).GetProperty("Name").GetCustomAttributes(false);
19
BrokenGlass

J'ai résolu des problèmes similaires en écrivant un assistant d'attribut de propriété d'extension générique:

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

public static class AttributeHelper
{
    public static TValue GetPropertyAttributeValue<T, TOut, TAttribute, TValue>(
        Expression<Func<T, TOut>> propertyExpression, 
        Func<TAttribute, TValue> valueSelector) 
        where TAttribute : Attribute
    {
        var expression = (MemberExpression) propertyExpression.Body;
        var propertyInfo = (PropertyInfo) expression.Member;
        var attr = propertyInfo.GetCustomAttributes(typeof(TAttribute), true).FirstOrDefault() as TAttribute;
        return attr != null ? valueSelector(attr) : default(TValue);
    }
}

Usage:

var author = AttributeHelper.GetPropertyAttributeValue<Book, string, AuthorAttribute, string>(prop => prop.Name, attr => attr.Author);
// author = "AuthorName"
17
Mikael Engver

Si vous voulez dire "pour les attributs qui prennent un paramètre, listez les noms d'attribut et la valeur de paramètre", c'est plus facile dans .NET 4.5 via l'API CustomAttributeData:

using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;

public static class Program
{
    static void Main()
    {
        PropertyInfo prop = typeof(Foo).GetProperty("Bar");
        var vals = GetPropertyAttributes(prop);
        // has: DisplayName = "abc", Browsable = false
    }
    public static Dictionary<string, object> GetPropertyAttributes(PropertyInfo property)
    {
        Dictionary<string, object> attribs = new Dictionary<string, object>();
        // look for attributes that takes one constructor argument
        foreach (CustomAttributeData attribData in property.GetCustomAttributesData()) 
        {

            if(attribData.ConstructorArguments.Count == 1)
            {
                string typeName = attribData.Constructor.DeclaringType.Name;
                if (typeName.EndsWith("Attribute")) typeName = typeName.Substring(0, typeName.Length - 9);
                attribs[typeName] = attribData.ConstructorArguments[0].Value;
            }

        }
        return attribs;
    }
}

class Foo
{
    [DisplayName("abc")]
    [Browsable(false)]
    public string Bar { get; set; }
}
12
Marc Gravell
private static Dictionary<string, string> GetAuthors()
{
    return typeof(Book).GetProperties()
        .SelectMany(prop => prop.GetCustomAttributes())
        .OfType<AuthorAttribute>()
        .ToDictionary(attribute => attribute.Name, attribute => attribute.Name);
}
3
dee

Nécromancie. 
Pour ceux qui doivent encore maintenir .NET 2.0 ou ceux qui veulent le faire sans LINQ:

public static object GetAttribute(System.Reflection.MemberInfo mi, System.Type t)
{
    object[] objs = mi.GetCustomAttributes(t, true);

    if (objs == null || objs.Length < 1)
        return null;

    return objs[0];
}



public static T GetAttribute<T>(System.Reflection.MemberInfo mi)
{
    return (T)GetAttribute(mi, typeof(T));
}


public delegate TResult GetValue_t<in T, out TResult>(T arg1);

public static TValue GetAttributValue<TAttribute, TValue>(System.Reflection.MemberInfo mi, GetValue_t<TAttribute, TValue> value) where TAttribute : System.Attribute
{
    TAttribute[] objAtts = (TAttribute[])mi.GetCustomAttributes(typeof(TAttribute), true);
    TAttribute att = (objAtts == null || objAtts.Length < 1) ? default(TAttribute) : objAtts[0];
    // TAttribute att = (TAttribute)GetAttribute(mi, typeof(TAttribute));

    if (att != null)
    {
        return value(att);
    }
    return default(TValue);
}

Exemple d'utilisation:

System.Reflection.FieldInfo fi = t.GetField("PrintBackground");
wkHtmlOptionNameAttribute att = GetAttribute<wkHtmlOptionNameAttribute>(fi);
string name = GetAttributValue<wkHtmlOptionNameAttribute, string>(fi, delegate(wkHtmlOptionNameAttribute a){ return a.Name;});

ou simplement 

string aname = GetAttributValue<wkHtmlOptionNameAttribute, string>(fi, a => a.Name );
2
Stefan Steiger
public static class PropertyInfoExtensions
{
    public static TValue GetAttributValue<TAttribute, TValue>(this PropertyInfo prop, Func<TAttribute, TValue> value) where TAttribute : Attribute
    {
        var att = prop.GetCustomAttributes(
            typeof(TAttribute), true
            ).FirstOrDefault() as TAttribute;
        if (att != null)
        {
            return value(att);
        }
        return default(TValue);
    }
}

Usage:

 //get class properties with attribute [AuthorAttribute]
        var props = typeof(Book).GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(AuthorAttribute)));
            foreach (var prop in props)
            {
               string value = prop.GetAttributValue((AuthorAttribute a) => a.Name);
            }

ou:

 //get class properties with attribute [AuthorAttribute]
        var props = typeof(Book).GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(AuthorAttribute)));
        IList<string> values = props.Select(prop => prop.GetAttributValue((AuthorAttribute a) => a.Name)).Where(attr => attr != null).ToList();
1
Victor

Je cherche juste le bon endroit pour mettre ce morceau de code.

disons que vous avez la propriété suivante:

[Display(Name = "Solar Radiation (Average)", ShortName = "SolarRadiationAvg")]
public int SolarRadiationAvgSensorId { get; set; }

Et vous voulez obtenir la valeur ShortName. Tu peux faire:

((DisplayAttribute)(typeof(SensorsModel).GetProperty(SolarRadiationAvgSensorId).GetCustomAttribute(typeof(DisplayAttribute)))).ShortName;

Ou pour le dire en général:

internal static string GetPropertyAttributeShortName(string propertyName)
{
    return ((DisplayAttribute)(typeof(SensorsModel).GetProperty(propertyName).GetCustomAttribute(typeof(DisplayAttribute)))).ShortName;
}
0
Asaf

pour obtenir l'attribut d'énum, ​​j'utilise:

 public enum ExceptionCodes
 {
  [ExceptionCode(1000)]
  InternalError,
 }

 public static (int code, string message) Translate(ExceptionCodes code)
        {
            return code.GetType()
            .GetField(Enum.GetName(typeof(ExceptionCodes), code))
            .GetCustomAttributes(false).Where((attr) =>
            {
                return (attr is ExceptionCodeAttribute);
            }).Select(customAttr =>
            {
                var attr = (customAttr as ExceptionCodeAttribute);
                return (attr.Code, attr.FriendlyMessage);
            }).FirstOrDefault();
        }

// En utilisant

 var _message = Translate(code);
0
Mohamed.Abdo

Bien que les réponses les plus citées ci-dessus fonctionnent définitivement, je suggérerais d'utiliser une approche légèrement différente dans certains cas.

Si votre classe a plusieurs propriétés avec toujours le même attribut et que vous souhaitez obtenir ces attributs triés dans un dictionnaire, voici comment:

var dict = typeof(Book).GetProperties().ToDictionary(p => p.Name, p => p.GetCustomAttributes(typeof(AuthorName), false).Select(a => (AuthorName)a).FirstOrDefault());

Cela utilise toujours le transtypage mais garantit que le transtypage fonctionnera toujours car vous obtiendrez uniquement les attributs personnalisés du type "AuthorName". Si vous aviez plusieurs attributs, les réponses ci-dessus auraient une exception de distribution.

0
Mirko Brandt
foreach (var p in model.GetType().GetProperties())
{
   var valueOfDisplay = 
       p.GetCustomAttributesData()
        .Any(a => a.AttributeType.Name == "DisplayNameAttribute") ? 
            p.GetCustomAttribute<DisplayNameAttribute>().DisplayName : 
            p.Name;
}

Dans cet exemple, j'ai utilisé DisplayName au lieu de Author car il comporte un champ nommé 'DisplayName' à afficher avec une valeur.

0
petrosmm

Voici quelques méthodes statiques que vous pouvez utiliser pour obtenir MaxLength ou tout autre attribut.

using System;
using System.Linq;
using System.Reflection;
using System.ComponentModel.DataAnnotations;
using System.Linq.Expressions;

public static class AttributeHelpers {

public static Int32 GetMaxLength<T>(Expression<Func<T,string>> propertyExpression) {
    return GetPropertyAttributeValue<T,string,MaxLengthAttribute,Int32>(propertyExpression,attr => attr.Length);
}

//Optional Extension method
public static Int32 GetMaxLength<T>(this T instance,Expression<Func<T,string>> propertyExpression) {
    return GetMaxLength<T>(propertyExpression);
}


//Required generic method to get any property attribute from any class
public static TValue GetPropertyAttributeValue<T, TOut, TAttribute, TValue>(Expression<Func<T,TOut>> propertyExpression,Func<TAttribute,TValue> valueSelector) where TAttribute : Attribute {
    var expression = (MemberExpression)propertyExpression.Body;
    var propertyInfo = (PropertyInfo)expression.Member;
    var attr = propertyInfo.GetCustomAttributes(typeof(TAttribute),true).FirstOrDefault() as TAttribute;

    if (attr==null) {
        throw new MissingMemberException(typeof(T).Name+"."+propertyInfo.Name,typeof(TAttribute).Name);
    }

    return valueSelector(attr);
}

}

En utilisant la méthode statique ...

var length = AttributeHelpers.GetMaxLength<Player>(x => x.PlayerName);

Ou en utilisant la méthode d'extension optionnelle sur une instance ...

var player = new Player();
var length = player.GetMaxLength(x => x.PlayerName);

Ou en utilisant la méthode statique complète pour tout autre attribut (StringLength par exemple) ...

var length = AttributeHelpers.GetPropertyAttributeValue<Player,string,StringLengthAttribute,Int32>(prop => prop.PlayerName,attr => attr.MaximumLength);

Inspiré par la réponse de Mikael Engver.

0
Carter Medlin