反射-获取属性的名称和值


253

我有一个类,可以使用名为Name的属性将其命名为Book。有了该属性,我就有了与之关联的属性。

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

在我的主要方法中,我正在使用反射,并希望为每个属性获取每个属性的键值对。因此,在此示例中,我希望属性名称看到“ Author”,属性值看到“ AuthorName”。

问题:如何使用反射获得属性名称和属性值?


当您尝试通过反射访问该对象上的属性时发生了什么情况,是卡在某个位置还是要进行反射的代码
Kobe

Answers:


307

使用typeof(Book).GetProperties()获得的阵列PropertyInfo实例。然后GetCustomAttributes()对每个对象使用,PropertyInfo以查看它们是否具有AuthorAttribute类型。如果这样做,则可以从属性信息中获取属性的名称,并从属性中获取属性值。

沿着这些思路进行一些操作,以扫描类型以查找具有特定属性类型的属性,并在字典中返回数据(请注意,可以通过将类型传递给例程来使其更加动态):

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
我希望我不必强制转换该属性。
developerdoug

prop.GetCustomAttributes(true)仅返回一个对象[]。如果您不想进行转换,则可以对属性实例本身使用反射。
亚当·马克维兹

什么是AuthorAttribute?它是从Attribute派生的类吗?@Adam Markowitz
Sarath Avanavu 2014年

1
是。OP正在使用名为“作者”的自定义属性。参见以下示例: msdn.microsoft.com/en-us/library/sw480ze8.aspx
Adam Markowitz

1
与其他所有涉及的操作(空检查和字符串分配除外)相比,强制转换属性的性能开销是微不足道的。
SilentSin

112

要获取字典中某个属性的所有属性,请使用以下命令:

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

记得从改变falsetrue,如果你想包括继承了属性为好。


3
这实际上与Adam的解决方案具有相同的作用,但更为简洁。
丹尼尔·摩尔

31
如果只需要Author属性并且想跳过将来的类型转换,则将.OfType <AuthorAttribue>()附加到表达式而不是ToDictionary上
Adrian Zanescu 2013年

2
当同一属性上有两个相同类型的属性时,会不会抛出异常?
康斯坦丁

53

如果只需要一个特定的属性值,例如“显示属性”,则可以使用以下代码:

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

30

我已经通过编写通用扩展属性帮助器解决了类似的问题:

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"

1
如何从const获取描述属性Fields
阿米尔(Amir)

1
您将得到:错误1775成员'Namespace.FieldName'无法通过实例引用进行访问;而是使用类型名称对其进行限定。如果您需要这样做,我建议将“ const”更改为“ readonly”。
Mikael Engver,2015年

1
老实说,您应该拥有比这更有用的选票。对于许多情况,这是一个非常不错且有用的答案。
DavidLétourneau'16

1
谢谢@DavidLétourneau!一个人只能希望。似乎您对此有所帮助。
Mikael Engver,2013年

:)您是否认为可以通过使用通用方法为一个类获取所有属性的值并将该属性的值分配给每个属性?
DavidLétourneau'16


12

如果您的意思是“对于带有一个参数的属性,列出属性名称和参数值”,那么在.NET 4.5中通过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

尽管以上最受好评的答案肯定有效,但我建议在某些情况下使用稍微不同的方法。

如果您的类具有始终具有相同属性的多个属性,并且您想将这些属性排序到字典中,则方法如下:

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

它仍然使用强制类型转换,但可以确保强制类型转换始终有效,因为您只会获得类型为“ AuthorName”的自定义属性。如果您在上面具有多个属性,答案将强制转换。


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

用法:

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

1

这是一些静态方法,可用于获取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);
}

}

使用静态方法...

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

或在实例上使用可选的扩展方法...

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

或者将完全静态方法用于任何其他属性(例如,StringLength)...

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

受到Mikael Engver的回答的启发。


1

死灵法师。
对于那些仍然需要维护.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 );

0
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'的字段要显示一个值。


0

从枚举获取属性,我正在使用:

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

0

只是在寻找合适的位置放置这段代码。

假设您具有以下属性:

[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;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.