Come leggo un attributo su una classe in fase di esecuzione?


107

Sto cercando di creare un metodo generico che leggerà un attributo su una classe e restituirà quel valore in fase di esecuzione. Come lo farei?

Nota: l'attributo DomainName è della classe DomainNameAttribute.

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

Quello che sto cercando di generare:

//This should return "MyTable"
String DomainNameValue = GetDomainName<MyClass>();


2
Importante domanda corollario come ottenere tutti i tipi di montaggio con attributo personalizzato stackoverflow.com/questions/2656189/...
Chris Marisic

Risposte:


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

AGGIORNARE:

Questo metodo potrebbe essere ulteriormente generalizzato per funzionare con qualsiasi attributo:

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

e usa in questo modo:

string name = typeof(MyClass)
    .GetAttributeValue((DomainNameAttribute dna) => dna.Name);

6
Grazie per la tua diligenza nel rispondere alla domanda!
Zaffiro

1
Questo metodo di estensione potrebbe essere ulteriormente generalizzato estendendo MemberInfo, una classe base di Type e tutti, o almeno la maggior parte , dei membri di un Type. In questo modo si aprirà per consentire la lettura degli attributi da Proprietà, Campi e persino Eventi.
M.Babcock

4
Troppo complicato. Non è necessario utilizzare lambda per selezionare il valore dell'attributo. Se hai abbastanza per scrivere lambda, ne sai abbastanza per accedere al campo.
Darrel Lee

Come posso estendere questo approccio per entrare const Filednella classe statica?
Amir

51

C'è già un'estensione per farlo.

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

Così:

var attr = typeof(MyClass).GetCustomAttribute<DomainNameAttribute>(false);
return attr != null ? attr.DomainName : "";

1
Vero. Ma solo .NET 4.5 e versioni successive. Sto ancora sviluppando il codice della libreria in cui non posso utilizzare questo metodo :(
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
E +1 per non usare "var", quindi è facile capire come funziona.
RenniePet

Non si compila. Ma "System.Reflection.MemberInfo info = typeof (MyClass) .GetTypeInfo ();" fare
Marcel James

4

Ho usato la risposta di Darin Dimitrov per creare un'estensione generica per ottenere attributi di membro per qualsiasi membro in una classe (invece di attributi per una classe). Lo pubblico qui perché altri potrebbero trovarlo utile:

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

Esempio di utilizzo:

//Read System.ComponentModel Description Attribute from method 'MyMethodName' in class 'MyClass'
var Attribute = typeof(MyClass).GetAttribute("MyMethodName", (DescriptionAttribute d) => d.Description);

Eredità non funziona sulle proprietà derivati - per questo, è necessario chiamare un metodo statico separato (System.Attribute.GetCustomAttributes) stackoverflow.com/a/7175762/184910
murraybiscuit

3

Una versione semplificata della prima soluzione di Darin Dimitrov:

public string GetDomainName<T>()
{
    var dnAttribute = typeof(T).GetCustomAttribute<DomainNameAttribute>(true);
    if (dnAttribute != null)
    {
        return dnAttribute.Name;
    }
    return null;
}


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

Probabilmente altrettanto facile da usare il corpo della funzione generica in linea. Non ha alcun senso per me rendere la funzione generica rispetto al tipo MyClass.

string DomainName = GetAttribute<DomainNameAttribute>(typeof(MyClass)).Name
// null reference exception if MyClass doesn't have the attribute.

0

Nel caso in cui qualcuno abbia bisogno di un risultato nullable e affinché funzioni su Enums, PropertyInfo e classi, ecco come l'ho risolto. Questa è una modifica della soluzione aggiornata di Darin Dimitrov.

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

Esempi di utilizzo:

// 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

Piuttosto che scrivere molto codice, fai semplicemente questo:

{         
   dynamic tableNameAttribute = typeof(T).CustomAttributes.FirstOrDefault().ToString();
   dynamic tableName = tableNameAttribute.Substring(tableNameAttribute.LastIndexOf('.'), tableNameAttribute.LastIndexOf('\\'));    
}

0

Quando si hanno metodi sostituiti con lo stesso nome, utilizzare l'helper di seguito

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

Utilizzo: var someController = new SomeController (Some params); var str = typeof (SomeController) .GetControllerMethodAttributeValue (x => someController.SomeMethod (It.IsAny ()), (RouteAttribute routeAttribute) => routeAttribute.Template);

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.