Отражение - получить имя атрибута и значение свойства - PullRequest
209 голосов
/ 10 июля 2011

У меня есть класс, давайте назовем его Book со свойством Name. С этим свойством у меня есть атрибут, связанный с ним.

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

В моем основном методе я использую отражение и хочу получить пару значений ключа каждого атрибута для каждого свойства. Поэтому в этом примере я ожидаю увидеть «Author» для имени атрибута и «AuthorName» для значения атрибута.

Вопрос: Как получить имя и значение атрибута в моих свойствах с помощью Reflection?

Ответы [ 14 ]

255 голосов
/ 10 июля 2011

Используйте typeof(Book).GetProperties(), чтобы получить массив PropertyInfo экземпляров. Затем используйте GetCustomAttributes() для каждого PropertyInfo, чтобы увидеть, имеет ли какой-либо из них тип атрибута Author. Если это так, вы можете получить имя свойства из информации свойства и значения атрибута из атрибута.

Что-то вроде этих строк для сканирования типа на предмет свойств, имеющих определенный тип атрибута, и для возврата данных в словарь (обратите внимание, что это можно сделать более динамичным, передавая типы в подпрограмму):

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;
}
94 голосов
/ 10 июля 2011

Чтобы получить все атрибуты свойства в словаре, используйте это:

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

не забудьте изменить с ложного на истинное, если вы хотите также включить унаследованные атрибуты.

43 голосов
/ 03 сентября 2014

Если вам просто нужно одно конкретное значение атрибута. Например, атрибут отображения, вы можете использовать следующий код:

var pInfo = typeof(Book).GetProperty("Name")
                             .GetCustomAttribute<DisplayAttribute>();
var name = pInfo.Name;
21 голосов
/ 10 сентября 2015

Я решил аналогичные проблемы, написав вспомогательный атрибут атрибута универсального расширения:

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);
    }
}

Использование:

var author = AttributeHelper.GetPropertyAttributeValue<Book, string, AuthorAttribute, string>(prop => prop.Name, attr => attr.Author);
// author = "AuthorName"
19 голосов
/ 10 июля 2011

Вы можете использовать GetCustomAttributesData() и GetCustomAttributes():

var attributeData = typeof(Book).GetProperty("Name").GetCustomAttributesData();
var attributes = typeof(Book).GetProperty("Name").GetCustomAttributes(false);
12 голосов
/ 26 октября 2012

Если вы имеете в виду «для атрибутов, которые принимают один параметр, перечислите имена атрибутов и значение параметра», то это проще в .NET 4.5 через CustomAttributeData API:

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; }
}
3 голосов
/ 10 июня 2016
private static Dictionary<string, string> GetAuthors()
{
    return typeof(Book).GetProperties()
        .SelectMany(prop => prop.GetCustomAttributes())
        .OfType<AuthorAttribute>()
        .ToDictionary(attribute => attribute.Name, attribute => attribute.Name);
}
2 голосов
/ 12 июня 2017

Necromancing.Для тех, кому еще нужно поддерживать .NET 2.0, или тех, кто хочет сделать это без 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);
}

Пример использования:

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

или просто

string aname = GetAttributValue<wkHtmlOptionNameAttribute, string>(fi, a => a.Name );
1 голос
/ 12 ноября 2015
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);
    }
}

Использование:

 //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);
            }

или:

 //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();
0 голосов
/ 05 февраля 2019

Хотя приведенные выше ответы с наибольшим количеством голосов определенно работают, я бы предложил использовать несколько иной подход в некоторых случаях.

Если у вашего класса есть несколько свойств с всегда одинаковым атрибутом, и вы хотите отсортировать эти атрибутыв словарь вот как:

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

Это все еще использует приведение, но гарантирует, что приведение будет работать всегда, так как вы получите только пользовательские атрибуты типа "AuthorName".Если у вас есть несколько атрибутов выше, ответы получат исключение приведения.

...