Làm cách nào để đọc thuộc tính trên một lớp trong thời gian chạy?


106

Tôi đang cố gắng tạo một phương thức chung sẽ đọc một thuộc tính trên một lớp và trả về giá trị đó trong thời gian chạy. Tôi sẽ làm điều này như thế nào?

Lưu ý: Thuộc tính DomainName thuộc lớp DomainNameAttribute.

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

Những gì tôi đang cố gắng tạo:

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

1
Liên kết chính thức của microsoft: msdn.microsoft.com/en-us/library/71s1zwct.aspx
Mahesh

2
Câu hỏi hệ quả quan trọng như thế nào để có được tất cả các loại trong lắp ráp với thuộc tính tùy chỉnh stackoverflow.com/questions/2656189/...
Chris Marisic

Câu trả lời:


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

CẬP NHẬT:

Phương pháp này có thể được tổng quát hóa thêm để hoạt động với bất kỳ thuộc tính nào:

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

và sử dụng như thế này:

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

6
Cảm ơn vì sự siêng năng của bạn trong việc trả lời câu hỏi!
Zaffiro

1
Phương thức mở rộng này có thể được tổng quát hóa hơn nữa bằng cách mở rộng MemberInfo, một lớp cơ sở của Loại và tất cả - hoặc ít nhất là hầu hết - các thành viên của Loại. Làm như vậy sẽ mở ra để cho phép đọc các thuộc tính từ Thuộc tính, Trường và thậm chí là Sự kiện.
M.Babcock

4
Quá phức tạp. Không cần sử dụng lambda để chọn giá trị thuộc tính. Nếu bạn đủ để viết lambda, bạn biết đủ để chỉ cần truy cập vào trường.
Darrel Lee

Làm thế nào tôi có thể mở rộng cách tiếp cận này để có được const Filedtrong lớp tĩnh?
Amir

51

Đã có một phần mở rộng để làm điều này.

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

Vì thế:

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

1
Thật. Nhưng chỉ .NET 4.5 và mới hơn. Tôi vẫn đang phát triển mã thư viện mà tôi không thể sử dụng phương pháp này :(
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
Và +1 vì không sử dụng "var", vì vậy rất dễ hiểu cách hoạt động của nó.
RenniePet

Nó không biên dịch. Nhưng "System.Reflection.MemberInfo info = typeof (MyClass) .GetTypeInfo ();" làm
Marcel James

4

Tôi đã sử dụng câu trả lời của Darin Dimitrov để tạo một phần mở rộng chung để lấy các thuộc tính thành viên cho bất kỳ thành viên nào trong một lớp (thay vì các thuộc tính cho một lớp). Tôi đăng nó ở đây vì những người khác có thể thấy nó hữu ích:

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

Ví dụ sử dụng:

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

Thừa kế không làm việc trên đặc tính có nguồn gốc - cho rằng, bạn sẽ cần phải gọi một phương thức tĩnh riêng biệt (System.Attribute.GetCustomAttributes) stackoverflow.com/a/7175762/184910
murraybiscuit

3

Một phiên bản đơn giản của giải pháp đầu tiên của 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

Có lẽ cũng dễ dàng sử dụng nội tuyến của hàm chung chung. Đối với tôi không có ý nghĩa gì khi làm cho hàm chung chung trên kiểu MyClass.

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

0

Trong trường hợp bất kỳ ai cần kết quả nullable và để điều này hoạt động trên Enums, PropertyInfo và các lớp, đây là cách tôi đã giải quyết nó. Đây là một sửa đổi của giải pháp cập nhật của 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;
    }
}

Các ví dụ sử dụng:

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

Thay vì viết rất nhiều mã, chỉ cần làm điều này:

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

0

Khi bạn có các phương thức bị ghi đè có cùng tên Sử dụng trình trợ giúp bên dưới

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

Cách sử dụng: var someController = new SomeController (Một số thông số); var str = typeof (SomeController) .GetControllerMethodAttributeValue (x => someController.SomeMethod (It.IsAny ()), (RouteAttribute routeAttribute) => routeAttribute.Template);

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.