Reflection - nhận tên và giá trị thuộc tính trên tài sản


253

Tôi có một lớp, hãy gọi nó là Sách với một thuộc tính được gọi là Tên. Với thuộc tính đó, tôi có một thuộc tính liên quan đến nó.

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

Trong phương thức chính của tôi, tôi đang sử dụng sự phản chiếu và muốn nhận được cặp giá trị chính của từng thuộc tính cho mỗi thuộc tính. Vì vậy, trong ví dụ này, tôi hy vọng sẽ thấy "Tác giả" cho tên thuộc tính và "AuthorName" cho giá trị thuộc tính.

Câu hỏi: Làm thế nào để tôi có được tên và giá trị thuộc tính trên các thuộc tính của mình bằng Reflection?


Điều gì xảy ra khi bạn đang cố gắng truy cập thuộc tính vào đối tượng đó thông qua sự phản chiếu, bạn có bị mắc kẹt ở đâu đó không hoặc bạn muốn mã phản ánh
kobe

Câu trả lời:


307

Sử dụng typeof(Book).GetProperties()để có được một loạt các PropertyInfotrường hợp. Sau đó sử dụng GetCustomAttributes()từng cái PropertyInfođể xem có ai trong số họ có Authorloại Thuộc tính không. Nếu họ làm như vậy, bạn có thể lấy tên của thuộc tính từ thông tin thuộc tính và các giá trị thuộc tính từ thuộc tính.

Một cái gì đó dọc theo các dòng này để quét một loại cho các thuộc tính có loại thuộc tính cụ thể và để trả về dữ liệu trong từ điển (lưu ý rằng điều này có thể được thực hiện linh hoạt hơn bằng cách chuyển các loại vào thường trình):

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

16
Tôi đã hy vọng rằng tôi sẽ không phải bỏ thuộc tính.
nhà phát triển

prop.GetCustomAttribut (true) chỉ trả về một đối tượng []. Nếu bạn không muốn truyền thì bạn có thể sử dụng sự phản chiếu trên chính các thể hiện thuộc tính.
Adam Markowitz

AuthorAttribution ở đây là gì? Đây có phải là một lớp có nguồn gốc từ Thuộc tính?
@Adam

1
Đúng. OP đang sử dụng một thuộc tính tùy chỉnh có tên 'Tác giả'. Xem ở đây để biết ví dụ: msdn.microsoft.com/en-us/l Library / sw480ze8.aspx
Adam Markowitz

1
Chi phí hiệu năng của việc truyền thuộc tính là không đáng kể so với mọi hoạt động khác có liên quan (ngoại trừ kiểm tra null và gán chuỗi).
SilentSin

112

Để có được tất cả các thuộc tính của một thuộc tính trong từ điển, hãy sử dụng:

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

hãy nhớ thay đổi từ falseđến truenếu bạn muốn bao gồm các thuộc tính kế thừa là tốt.


3
Điều này thực sự giống như giải pháp của Adam, nhưng ngắn gọn hơn nhiều.
Daniel Moore

31
Nối .OfType <AuthorAttribue> () vào biểu thức thay vì ToDipedia nếu bạn chỉ cần thuộc tính Tác giả và muốn bỏ qua một diễn viên trong tương lai
Adrian Zanescu

2
Sẽ không có ngoại lệ ném này khi có hai thuộc tính cùng loại trên cùng một thuộc tính?
Konstantin

53

Nếu bạn chỉ muốn một giá trị Thuộc tính cụ thể Ví dụ Thuộc tính Hiển thị, bạn có thể sử dụng mã sau:

var pInfo = typeof(Book).GetProperty("Name")
                             .GetCustomAttribute<DisplayAttribute>();
var name = pInfo.Name;

30

Tôi đã giải quyết các vấn đề tương tự bằng cách viết Trình trợ giúp thuộc tính thuộc tính mở rộng chung:

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

Sử dụng:

var author = AttributeHelper.GetPropertyAttributeValue<Book, string, AuthorAttribute, string>(prop => prop.Name, attr => attr.Author);
// author = "AuthorName"

1
Làm thế nào tôi có thể nhận được thuộc tính mô tả từ const Fields?
Amir

1
Bạn sẽ nhận được: Lỗi 1775 Thành viên 'Namespace.FieldName' không thể được truy cập với một tham chiếu thể hiện; đủ điều kiện với một loại tên thay thế. Nếu bạn cần làm điều này, tôi đề nghị thay đổi 'const' thành 'chỉ đọc'.
Mikael Engver

1
Thành thật mà nói, bạn nên có nhiều phiếu bầu hữu ích hơn thế. Đó là một câu trả lời rất hay và hữu ích cho nhiều trường hợp.
David Létourneau

1
Cảm ơn @ DavidLétourneau! Người ta chỉ có thể hy vọng. Có vẻ như bạn đã giúp một chút trong đó.
Mikael Engver

:) Bạn có nghĩ rằng có thể có giá trị của tất cả các thuộc tính cho một lớp bằng cách sử dụng phương thức chung của bạn và gán giá trị của thuộc tính cho từng thuộc tính không?
David Létourneau

21

Bạn có thể sử dụng GetCustomAttributesData()GetCustomAttributes():

var attributeData = typeof(Book).GetProperty("Name").GetCustomAttributesData();
var attributes = typeof(Book).GetProperty("Name").GetCustomAttributes(false);

4
có gì khác biệt?
Prime By Design

1
@PrimeByDesign Trước đây tìm hiểu cách khởi tạo các thuộc tính được áp dụng. Cái sau thực sự khởi tạo những thuộc tính đó.
HappyNomad

12

Nếu bạn có nghĩa là "đối với các thuộc tính lấy một tham số, hãy liệt kê tên thuộc tính và giá trị tham số", thì điều này dễ dàng hơn trong .NET 4.5 thông qua CustomAttributeDataAPI:

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
private static Dictionary<string, string> GetAuthors()
{
    return typeof(Book).GetProperties()
        .SelectMany(prop => prop.GetCustomAttributes())
        .OfType<AuthorAttribute>()
        .ToDictionary(attribute => attribute.Name, attribute => attribute.Name);
}

2

Mặc dù các câu trả lời được đánh giá cao nhất ở trên chắc chắn có hiệu quả, tôi khuyên bạn nên sử dụng một cách tiếp cận hơi khác trong một số trường hợp.

Nếu lớp của bạn có nhiều thuộc tính luôn có cùng thuộc tính và bạn muốn nhận các thuộc tính đó được sắp xếp vào từ điển, thì đây là cách:

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

Điều này vẫn sử dụng cast nhưng đảm bảo rằng cast sẽ luôn hoạt động vì bạn sẽ chỉ nhận được các thuộc tính tùy chỉnh của loại "AuthorName". Nếu bạn có nhiều thuộc tính trên, câu trả lời sẽ có ngoại lệ.


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

Sử dụng:

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

hoặc là:

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

1

Dưới đây là một số phương thức tĩnh bạn có thể sử dụng để lấy MaxLạng hoặc bất kỳ thuộc tính nào khác.

using System;
using System.Linq;
using System.Reflection;
using System.ComponentModel.DataAnnotations;
using System.Linq.Expressions;

public static class AttributeHelpers {

public static Int32 GetMaxLength<T>(Expression<Func<T,string>> propertyExpression) {
    return GetPropertyAttributeValue<T,string,MaxLengthAttribute,Int32>(propertyExpression,attr => attr.Length);
}

//Optional Extension method
public static Int32 GetMaxLength<T>(this T instance,Expression<Func<T,string>> propertyExpression) {
    return GetMaxLength<T>(propertyExpression);
}


//Required generic method to get any property attribute from any class
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;

    if (attr==null) {
        throw new MissingMemberException(typeof(T).Name+"."+propertyInfo.Name,typeof(TAttribute).Name);
    }

    return valueSelector(attr);
}

}

Sử dụng phương thức tĩnh ...

var length = AttributeHelpers.GetMaxLength<Player>(x => x.PlayerName);

Hoặc sử dụng phương thức tiện ích mở rộng tùy chọn trên một ví dụ ...

var player = new Player();
var length = player.GetMaxLength(x => x.PlayerName);

Hoặc sử dụng phương thức tĩnh đầy đủ cho bất kỳ thuộc tính nào khác (ví dụ StringLpm) ...

var length = AttributeHelpers.GetPropertyAttributeValue<Player,string,StringLengthAttribute,Int32>(prop => prop.PlayerName,attr => attr.MaximumLength);

Lấy cảm hứng từ câu trả lời của Mikael Engver.


1

Necromance.
Đối với những người vẫn phải duy trì .NET 2.0 hoặc những người muốn làm điều đó mà không có 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);
}

Ví dụ sử dụng:

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

hoặc đơn giản

string aname = GetAttributValue<wkHtmlOptionNameAttribute, string>(fi, a => a.Name );

0
foreach (var p in model.GetType().GetProperties())
{
   var valueOfDisplay = 
       p.GetCustomAttributesData()
        .Any(a => a.AttributeType.Name == "DisplayNameAttribute") ? 
            p.GetCustomAttribute<DisplayNameAttribute>().DisplayName : 
            p.Name;
}

Trong ví dụ này, tôi đã sử dụng DisplayName thay vì Author vì nó có một trường có tên 'DisplayName' để được hiển thị với một giá trị.


0

để có được thuộc tính từ enum, tôi đang sử dụng:

 public enum ExceptionCodes
 {
  [ExceptionCode(1000)]
  InternalError,
 }

 public static (int code, string message) Translate(ExceptionCodes code)
        {
            return code.GetType()
            .GetField(Enum.GetName(typeof(ExceptionCodes), code))
            .GetCustomAttributes(false).Where((attr) =>
            {
                return (attr is ExceptionCodeAttribute);
            }).Select(customAttr =>
            {
                var attr = (customAttr as ExceptionCodeAttribute);
                return (attr.Code, attr.FriendlyMessage);
            }).FirstOrDefault();
        }

// Sử dụng

 var _message = Translate(code);

0

Chỉ cần tìm đúng nơi để đặt đoạn mã này.

giả sử bạn có tài sản sau:

[Display(Name = "Solar Radiation (Average)", ShortName = "SolarRadiationAvg")]
public int SolarRadiationAvgSensorId { get; set; }

Và bạn muốn lấy giá trị ShortName. Bạn có thể làm:

((DisplayAttribute)(typeof(SensorsModel).GetProperty(SolarRadiationAvgSensorId).GetCustomAttribute(typeof(DisplayAttribute)))).ShortName;

Hoặc để làm cho nó chung chung:

internal static string GetPropertyAttributeShortName(string propertyName)
{
    return ((DisplayAttribute)(typeof(SensorsModel).GetProperty(propertyName).GetCustomAttribute(typeof(DisplayAttribute)))).ShortName;
}
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.