Как прочитать атрибут класса во время выполнения?

107

Я пытаюсь создать универсальный метод, который будет считывать атрибут класса и возвращать это значение во время выполнения. Как мне это сделать?

Примечание. Атрибут DomainName относится к классу DomainNameAttribute.

[DomainName("MyTable")]
Public class MyClass : DomainBase
{}

Что я пытаюсь создать:

//This should return "MyTable"
String DomainNameValue = GetDomainName<MyClass>();
Заффиро
источник
1
Официальная ссылка на Microsoft: msdn.microsoft.com/en-us/library/71s1zwct.aspx
Махеш,
2
Важный сопутствующий вопрос, как получить все типы в сборке с настраиваемым атрибутом stackoverflow.com/questions/2656189/…
Крис Марисич

Ответы:

236
public string GetDomainName<T>()
{
    var dnAttribute = typeof(T).GetCustomAttributes(
        typeof(DomainNameAttribute), true
    ).FirstOrDefault() as DomainNameAttribute;
    if (dnAttribute != null)
    {
        return dnAttribute.Name;
    }
    return null;
}

ОБНОВИТЬ:

Этот метод можно обобщить для работы с любым атрибутом:

public static class AttributeExtensions
{
    public static TValue GetAttributeValue<TAttribute, TValue>(
        this Type type, 
        Func<TAttribute, TValue> valueSelector) 
        where TAttribute : Attribute
    {
        var att = type.GetCustomAttributes(
            typeof(TAttribute), true
        ).FirstOrDefault() as TAttribute;
        if (att != null)
        {
            return valueSelector(att);
        }
        return default(TValue);
    }
}

и используйте вот так:

string name = typeof(MyClass)
    .GetAttributeValue((DomainNameAttribute dna) => dna.Name);
Дарин Димитров
источник
6
Спасибо за усердие в ответе на вопрос!
Zaffiro
1
Этот метод расширения может быть дополнительно обобщен путем расширения MemberInfo, базового класса Type и всех - или, по крайней мере, большинства - членов Type. Это откроет доступ для чтения атрибутов из свойств, полей и даже событий.
M.Babcock
4
Слишком сложно. Для выбора значения атрибута нет необходимости использовать лямбда. Если вам достаточно, чтобы написать лямбду, вы знаете достаточно, чтобы просто получить доступ к полю.
Даррел Ли
Как я могу расширить этот подход, чтобы попасть const Filedв статический класс?
Амир
51

Для этого уже есть расширение.

namespace System.Reflection
{
    // Summary:
    //     Contains static methods for retrieving custom attributes.
    public static class CustomAttributeExtensions
    {
        public static T GetCustomAttribute<T>(this MemberInfo element, bool inherit) where T : Attribute;
    }
}

Так:

var attr = typeof(MyClass).GetCustomAttribute<DomainNameAttribute>(false);
return attr != null ? attr.DomainName : "";
Даррел Ли
источник
1
Правда. Но только .NET 4.5 и новее. Я все еще разрабатываю код библиотеки, в котором я не могу использовать этот метод :(
andreas
15
System.Reflection.MemberInfo info = typeof(MyClass);
object[] attributes = info.GetCustomAttributes(true);

for (int i = 0; i < attributes.Length; i++)
{
    if (attributes[i] is DomainNameAttribute)
    {
        System.Console.WriteLine(((DomainNameAttribute) attributes[i]).Name);
    }   
}
Мерритт
источник
5
И +1 за то, что не использовал var, так что легко понять, как это работает.
RenniePet
Он не компилируется. Но «System.Reflection.MemberInfo info = typeof (MyClass) .GetTypeInfo ();» do
Марсель Джеймс
4

Я использовал ответ Дарина Димитрова для создания универсального расширения для получения атрибутов членов для любого члена в классе (вместо атрибутов для класса). Я размещаю его здесь, потому что другие могут найти его полезным:

public static class AttributeExtensions
{
    /// <summary>
    /// Returns the value of a member attribute for any member in a class.
    ///     (a member is a Field, Property, Method, etc...)    
    /// <remarks>
    /// If there is more than one member of the same name in the class, it will return the first one (this applies to overloaded methods)
    /// </remarks>
    /// <example>
    /// Read System.ComponentModel Description Attribute from method 'MyMethodName' in class 'MyClass': 
    ///     var Attribute = typeof(MyClass).GetAttribute("MyMethodName", (DescriptionAttribute d) => d.Description);
    /// </example>
    /// <param name="type">The class that contains the member as a type</param>
    /// <param name="MemberName">Name of the member in the class</param>
    /// <param name="valueSelector">Attribute type and property to get (will return first instance if there are multiple attributes of the same type)</param>
    /// <param name="inherit">true to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events</param>
    /// </summary>    
    public static TValue GetAttribute<TAttribute, TValue>(this Type type, string MemberName, Func<TAttribute, TValue> valueSelector, bool inherit = false) where TAttribute : Attribute
    {
        var att = type.GetMember(MemberName).FirstOrDefault().GetCustomAttributes(typeof(TAttribute), inherit).FirstOrDefault() as TAttribute;
        if (att != null)
        {
            return valueSelector(att);
        }
        return default(TValue);
    }
}

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

//Read System.ComponentModel Description Attribute from method 'MyMethodName' in class 'MyClass'
var Attribute = typeof(MyClass).GetAttribute("MyMethodName", (DescriptionAttribute d) => d.Description);
Севин7
источник
Наследование не работает с производными свойствами - для этого вам нужно вызвать отдельный статический метод (System.Attribute.GetCustomAttributes) stackoverflow.com/a/7175762/184910
murraybiscuit
3

Упрощенная версия первого решения Дарина Димитрова:

public string GetDomainName<T>()
{
    var dnAttribute = typeof(T).GetCustomAttribute<DomainNameAttribute>(true);
    if (dnAttribute != null)
    {
        return dnAttribute.Name;
    }
    return null;
}
jk7
источник
0
' Simplified Generic version. 
Shared Function GetAttribute(Of TAttribute)(info As MemberInfo) As TAttribute
    Return info.GetCustomAttributes(GetType(TAttribute), _
                                    False).FirstOrDefault()
End Function

' Example usage over PropertyInfo
Dim fieldAttr = GetAttribute(Of DataObjectFieldAttribute)(pInfo)
If fieldAttr IsNot Nothing AndAlso fieldAttr.PrimaryKey Then
    keys.Add(pInfo.Name)
End If

Вероятно, так же просто использовать тело встроенной универсальной функции. Мне не имеет смысла делать функцию универсальной для типа MyClass.

string DomainName = GetAttribute<DomainNameAttribute>(typeof(MyClass)).Name
// null reference exception if MyClass doesn't have the attribute.
Даррел Ли
источник
0

Если кому-то нужен результат, допускающий значение NULL, и чтобы он работал в Enums, PropertyInfo и классах, вот как я решил это. Это модификация обновленного решения Дарина Димитрова.

public static object GetAttributeValue<TAttribute, TValue>(this object val, Func<TAttribute, TValue> valueSelector) where TAttribute : Attribute
{
    try
    {
        Type t = val.GetType();
        TAttribute attr;
        if (t.IsEnum && t.GetField(val.ToString()).GetCustomAttributes(typeof(TAttribute), true).FirstOrDefault() is TAttribute att)
        {
            // Applies to Enum values
            attr = att;
        }
        else if (val is PropertyInfo pi && pi.GetCustomAttributes(typeof(TAttribute), true).FirstOrDefault() is TAttribute piAtt)
        {
            // Applies to Properties in a Class
            attr = piAtt;
        }
        else
        {
            // Applies to classes
            attr = (TAttribute)t.GetCustomAttributes(typeof(TAttribute), false).FirstOrDefault();
        }
        return valueSelector(attr);
    }
    catch
    {
        return null;
    }
}

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

// Class
SettingsEnum.SettingGroup settingGroup = (SettingsEnum.SettingGroup)(this.GetAttributeValue((SettingGroupAttribute attr) => attr.Value) as SettingsEnum.SettingGroup?);

// Enum
DescriptionAttribute desc = settingGroup.GetAttributeValue((DescriptionAttribute attr) => attr) as DescriptionAttribute;

// PropertyInfo       
foreach (PropertyInfo pi in this.GetType().GetProperties())
{
    string setting = ((SettingsEnum.SettingName)(pi.GetAttributeValue((SettingNameAttribute attr) => attr.Value) as SettingsEnum.SettingName?)).ToString();
}
Мидеус
источник
0

Вместо того, чтобы писать много кода, просто сделайте следующее:

{         
   dynamic tableNameAttribute = typeof(T).CustomAttributes.FirstOrDefault().ToString();
   dynamic tableName = tableNameAttribute.Substring(tableNameAttribute.LastIndexOf('.'), tableNameAttribute.LastIndexOf('\\'));    
}
Наим Ахмед
источник
0

Если у вас есть переопределенные методы с тем же именем, используйте помощник ниже

public static TValue GetControllerMethodAttributeValue<T, TT, TAttribute, TValue>(this T type, Expression<Func<T, TT>> exp, Func<TAttribute, TValue> valueSelector) where TAttribute : Attribute
        {
            var memberExpression = exp?.Body as MethodCallExpression;

            if (memberExpression.Method.GetCustomAttributes(typeof(TAttribute), false).FirstOrDefault() is TAttribute attr && valueSelector != null)
            {
                return valueSelector(attr);
            }

            return default(TValue);
        }

Использование: var someController = new SomeController (некоторые параметры); var str = typeof (SomeController) .GetControllerMethodAttributeValue (x => someController.SomeMethod (It.IsAny ()), (RouteAttribute routeAttribute) => routeAttribute.Template);

Вамси Дж.
источник