Bagaimana cara membaca atribut pada kelas saat runtime?

107

Saya mencoba membuat metode umum yang akan membaca atribut di kelas dan mengembalikan nilai itu saat runtime. Bagaimana saya melakukan ini?

Catatan: Atribut DomainName adalah kelas DomainNameAttribute.

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

Apa yang saya coba hasilkan:

//This should return "MyTable"
String DomainNameValue = GetDomainName<MyClass>();
Zaffiro
sumber
1
Tautan resmi Microsoft: msdn.microsoft.com/en-us/library/71s1zwct.aspx
Mahesh
2
Pertanyaan wajar yang penting bagaimana mendapatkan semua jenis dalam perakitan dengan atribut khusus stackoverflow.com/questions/2656189/…
Chris Marisic

Jawaban:

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

MEMPERBARUI:

Metode ini dapat digeneralisasikan lebih lanjut untuk bekerja dengan atribut apa pun:

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

dan gunakan seperti ini:

string name = typeof(MyClass)
    .GetAttributeValue((DomainNameAttribute dna) => dna.Name);
Darin Dimitrov
sumber
6
Terima kasih atas ketekunan Anda dalam menjawab pertanyaan!
Zaffiro
1
Metode ekstensi ini dapat digeneralisasikan lebih lanjut dengan memperluas MemberInfo, kelas dasar Type dan semua - atau setidaknya sebagian besar - anggota Type. Melakukannya akan membukanya untuk memungkinkan membaca atribut dari Properties, Fields, dan bahkan Events.
M.Babcock
4
Terlalu rumit. Tidak perlu menggunakan lambda untuk memilih nilai atribut. Jika Anda cukup menulis lambda, Anda cukup tahu untuk hanya mengakses bidangnya.
Darrel Lee
Bagaimana saya bisa memperluas pendekatan ini untuk masuk ke const Filedkelas statis?
Amir
51

Sudah ada perpanjangan untuk melakukan ini.

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

Begitu:

var attr = typeof(MyClass).GetCustomAttribute<DomainNameAttribute>(false);
return attr != null ? attr.DomainName : "";
Darrel Lee
sumber
1
Benar. Tetapi hanya .NET 4.5 dan yang lebih baru. Saya masih mengembangkan kode perpustakaan di mana saya tidak dapat menggunakan metode ini :(
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);
    }   
}
Merritt
sumber
5
Dan +1 karena tidak menggunakan "var", jadi mudah untuk memahami cara kerjanya.
RenniePet
Itu tidak dapat dikompilasi. Tapi "System.Reflection.MemberInfo info = typeof (MyClass) .GetTypeInfo ();" lakukan
Marcel James
4

Saya menggunakan jawaban Darin Dimitrov untuk membuat ekstensi generik untuk mendapatkan atribut anggota untuk setiap anggota di kelas (bukan atribut untuk kelas). Saya mempostingnya di sini karena orang lain mungkin menganggapnya berguna:

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

Contoh penggunaan:

//Read System.ComponentModel Description Attribute from method 'MyMethodName' in class 'MyClass'
var Attribute = typeof(MyClass).GetAttribute("MyMethodName", (DescriptionAttribute d) => d.Description);
Sevin7
sumber
Pewarisan tidak berfungsi pada properti turunan - untuk itu, Anda harus memanggil metode statis terpisah (System.Attribute.GetCustomAttributes) stackoverflow.com/a/7175762/184910
murraybiscuit
3

Versi sederhana dari solusi pertama Darin Dimitrov:

public string GetDomainName<T>()
{
    var dnAttribute = typeof(T).GetCustomAttribute<DomainNameAttribute>(true);
    if (dnAttribute != null)
    {
        return dnAttribute.Name;
    }
    return null;
}
jk7
sumber
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

Mungkin semudah menggunakan tubuh fungsi generik sebaris. Tidak masuk akal bagi saya untuk membuat fungsi generik di atas tipe MyClass.

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

Jika ada yang membutuhkan hasil nullable dan agar ini berfungsi di Enums, PropertyInfo, dan kelas, berikut cara saya menyelesaikannya. Ini adalah modifikasi dari solusi terbaru 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;
    }
}

Contoh penggunaan:

// 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();
}
Mideus
sumber
0

Daripada menulis banyak kode, lakukan ini:

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

Jika Anda memiliki Metode yang Ditimpa dengan Nama yang sama Gunakan penolong di bawah ini

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

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

Vamsi J
sumber