关于c#:Reflection – 获取属性的属性名称和值

Reflection - get attribute name and value on property

我有一个类,我们称它为具有名属性的Book。对于这个属性,我有一个与之相关联的属性。

1
2
3
4
5
6
7
8
public class Book
{
    [Author("AuthorName")]
    public string Name
    {
        get; private set;
    }
}

在我的主要方法中,我使用反射,并希望为每个属性获取每个属性的键值对。所以在这个例子中,我希望看到属性名为"author",属性值为"author name"。

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


使用typeof(Book).GetProperties()获取PropertyInfo实例的数组。然后在每个PropertyInfo上使用GetCustomAttributes()查看它们中是否有任何一个具有Author属性类型。如果有,则可以从属性信息中获取属性名称,从属性中获取属性值。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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;
}


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

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

如果还想包含继承的属性,请记住将其更改为"假"或"真"。


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

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

您可以使用GetCustomAttributesData()GetCustomAttributes()

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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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);
    }
}

用途:

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


如果您的意思是"对于接受一个参数的属性,列出属性名和参数值",那么通过CustomAttributeDataAPI在.NET 4.5中更容易实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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; }
}


1
2
3
4
5
6
7
private static Dictionary<string, string> GetAuthors()
{
    return typeof(Book).GetProperties()
        .SelectMany(prop => prop.GetCustomAttributes())
        .OfType<AuthorAttribute>()
        .ToDictionary(attribute => attribute.Name, attribute => attribute.Name);
}

亡灵术。< BR>对于那些仍然需要维护.NET 2.0的用户,或者那些希望在没有LINQ的情况下进行维护的用户:

16

示例用法:

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

或者简单地

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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);
    }
}

用途:

1
2
3
4
5
6
 //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);
            }

或:

1
2
3
 //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
var dict = typeof(Book).GetProperties().ToDictionary(p => p.Name, p => p.GetCustomAttributes(typeof(AuthorName), false).Select(a => (AuthorName)a).FirstOrDefault());

这仍然使用cast,但可以确保该cast始终有效,因为您只能获取类型为"authorname"的自定义属性。如果您具有以上多个属性,那么答案将得到一个强制转换异常。


只是在寻找放置这段代码的正确位置。

假设您拥有以下属性:

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

你想得到短名称值。你可以做到:

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

或使其通用:

1
2
3
4
internal static string GetPropertyAttributeShortName(string propertyName)
{
    return ((DisplayAttribute)(typeof(SensorsModel).GetProperty(propertyName).GetCustomAttribute(typeof(DisplayAttribute)))).ShortName;
}


要从枚举中获取属性,我使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 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();
        }

/使用

1
 var _message = Translate(code);

这里有一些静态方法可以用来获取maxlength或任何其他属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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);
}

}

使用静态方法…

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

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

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

或者对任何其他属性使用完全静态方法(例如stringlength)…

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

灵感来自Mikael Engver的回答。


1
2
3
4
5
6
7
8
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"的字段要用值显示。