簡體   English   中英

反射 - 獲取屬性的屬性名稱和值

[英]Reflection - get attribute name and value on property

我有一個 class,我們稱它為 Book,並帶有一個名為 Name 的屬性。 有了這個屬性,我就有了一個與之關聯的屬性。

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

在我的主要方法中,我使用反射並希望獲取每個屬性的每個屬性的鍵值對。 所以在這個例子中,我希望看到屬性名稱的“作者”和屬性值的“作者名稱”。

問題:如何使用反射獲取我的屬性的屬性名稱和值?

使用typeof(Book).GetProperties()獲取PropertyInfo實例的數組。 然后在每個PropertyInfo上使用GetCustomAttributes()來查看它們中的任何一個是否具有Author Attribute 類型。 如果是這樣,您可以從屬性信息中獲取屬性的名稱,並從屬性中獲取屬性值。

沿着這些思路掃描具有特定屬性類型的屬性的類型並在字典中返回數據(請注意,這可以通過將類型傳遞到例程中變得更加動態):

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

要獲取字典中屬性的所有屬性,請使用以下命令:

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

如果您還想包含繼承的屬性,請記住從false更改為true

如果您只想要一個特定的屬性值,例如顯示屬性,您可以使用以下代碼:

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

我通過編寫通用擴展屬性屬性助手解決了類似的問題:

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

用法:

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

您可以使用GetCustomAttributesData()GetCustomAttributes()

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

如果您的意思是“對於采用一個參數的屬性,請列出屬性名稱和參數值”,那么在 .NET 4.5 中通過CustomAttributeData API 會更容易:

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

使用 generics 的示例(目標框架 4.5)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

private static Dictionary<string, string> GetAttribute<TAttribute, TType>(
    Func<TAttribute, string> valueFunc)
    where TAttribute : Attribute
{
    return typeof(TType).GetProperties()
        .SelectMany(p => p.GetCustomAttributes())
        .OfType<TAttribute>()
        .ToDictionary(a => a.GetType().Name.Replace("Attribute", ""), valueFunc);
}

用法

var dictionary = GetAttribute<AuthorAttribute, Book>(a => a.Name);
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);
    }
}

用法:

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

或者:

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

雖然上述最受好評的答案肯定有效,但我建議在某些情況下使用稍微不同的方法。

如果您的 class 具有多個屬性始終相同的屬性,並且您希望將這些屬性排序到字典中,則方法如下:

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

這仍然使用強制轉換,但確保強制轉換始終有效,因為您只會獲得“AuthorName”類型的自定義屬性。 如果您有多個以上答案的屬性,則會出現強制轉換異常。

我把它寫成一個動態方法,因為我在整個應用程序中使用了很多屬性。 方法:

public static dynamic GetAttribute(Type objectType, string propertyName, Type attrType)
    {
        //get the property
        var property = objectType.GetProperty(propertyName);

        //check for object relation
        return property.GetCustomAttributes().FirstOrDefault(x => x.GetType() == attrType);
    }

用法:

var objectRelAttr = GetAttribute(typeof(Person), "Country", typeof(ObjectRelationAttribute));

var displayNameAttr = GetAttribute(typeof(Product), "Category", typeof(DisplayNameAttribute));

希望這對任何人都有幫助

這里有一些 static 方法可以用來獲取 MaxLength 或任何其他屬性。

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

}

使用 static 方法...

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

或者在實例上使用可選的擴展方法......

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

或者對任何其他屬性(例如 StringLength)使用完整的 static 方法...

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

受到 Mikael Engver 的回答的啟發。

死靈術。
對於那些仍然需要維護 .NET 2.0 的人,或者那些想要在沒有 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);
}

示例用法:

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

或者干脆

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

只是在尋找放置這段代碼的正確位置。

假設您具有以下屬性:

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

並且您想要獲取 ShortName 值。 你可以做:

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

或使其通用:

internal static string GetPropertyAttributeShortName(string propertyName)
{
    return ((DisplayAttribute)(typeof(SensorsModel).GetProperty(propertyName).GetCustomAttribute(typeof(DisplayAttribute)))).ShortName;
}
foreach (var p in model.GetType().GetProperties())
{
   var valueOfDisplay = 
       p.GetCustomAttributesData()
        .Any(a => a.AttributeType.Name == "DisplayNameAttribute") ? 
            p.GetCustomAttribute<DisplayNameAttribute>().DisplayName : 
            p.Name;
}

在此示例中,我使用了 DisplayName 而不是 Author,因為它有一個名為“DisplayName”的字段要顯示一個值。

要從枚舉中獲取屬性,我正在使用:

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

// 使用

 var _message = Translate(code);

如果您想獲取具有自定義屬性的屬性,請嘗試以下操作:

IEnumerable propertyInfos = properties.GetType().GetProperties();

PropertyInfo p = propertyInfos.Where(x => x.GetCustomAttribute();= null);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM