Reflection - получить имя атрибута и значение свойства
У меня есть класс, давайте назовем его Book с свойством Name. С этим свойством у меня есть связанный с ним атрибут.
public class Book
{
[Author("AuthorName")]
public string Name
{
get; private set;
}
}
В моем основном методе я использую отражение и хочу получить пару ключевых значений каждого атрибута для каждого свойства. Поэтому в этом примере я ожидаю увидеть "Author" для имени атрибута и "AuthorName" для значения атрибута.
Вопрос: Как получить имя и значение атрибута в моих свойствах с помощью Reflection?
Ответы
Ответ 1
Используйте typeof(Book).GetProperties()
чтобы получить массив экземпляров PropertyInfo
. Затем используйте GetCustomAttributes()
для каждого PropertyInfo
чтобы увидеть, имеет ли какой-либо из них тип атрибута Author
. Если это так, вы можете получить имя свойства из информации о свойствах и значения атрибута из атрибута.
Что-то вроде этих строк, чтобы сканировать тип для свойств, которые имеют определенный тип атрибута, и возвращать данные в словаре (обратите внимание, что это можно сделать более динамичным, передавая типы в подпрограмму):
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;
}
Ответ 2
Чтобы получить все атрибуты свойства в словаре, используйте это:
typeof(Book)
.GetProperty("Name")
.GetCustomAttributes(false)
.ToDictionary(a => a.GetType().Name, a => a);
не забудьте перейти с false
на true
, если вы также хотите включить унаследованные атрибуты.
Ответ 3
Если вам просто нужно одно конкретное значение атрибута. Например, атрибут отображения, вы можете использовать следующий код:
var pInfo = typeof(Book).GetProperty("Name")
.GetCustomAttribute<DisplayAttribute>();
var name = pInfo.Name;
Ответ 4
Я решил подобные проблемы, написав помощник атрибута свойства универсального расширения:
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"
Ответ 5
Вы можете использовать GetCustomAttributesData()
и GetCustomAttributes()
:
var attributeData = typeof(Book).GetProperty("Name").GetCustomAttributesData();
var attributes = typeof(Book).GetProperty("Name").GetCustomAttributes(false);
Ответ 6
Если вы имеете в виду "для атрибутов, которые принимают один параметр, укажите имена атрибутов и значение параметра", то это проще в .NET 4.5 с помощью API CustomAttributeData
:
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; }
}
Ответ 7
private static Dictionary<string, string> GetAuthors()
{
return typeof(Book).GetProperties()
.SelectMany(prop => prop.GetCustomAttributes())
.OfType<AuthorAttribute>()
.ToDictionary(attribute => attribute.Name, attribute => attribute.Name);
}
Ответ 8
Necromancing.
Для тех, кому еще нужно поддерживать .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 );
Ответ 9
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();
Ответ 10
Хотя приведенные выше ответы с наибольшим количеством голосов определенно работают, я бы предложил использовать несколько иной подход в некоторых случаях.
Если ваш класс имеет несколько свойств с всегда одним и тем же атрибутом, и вы хотите отсортировать эти атрибуты в словаре, вот как:
var dict = typeof(Book).GetProperties().ToDictionary(p => p.Name, p => p.GetCustomAttributes(typeof(AuthorName), false).Select(a => (AuthorName)a).FirstOrDefault());
Это все еще использует приведение, но гарантирует, что приведение будет всегда работать, поскольку вы получите только пользовательские атрибуты типа "AuthorName". Если у вас есть несколько атрибутов выше, ответы получат исключение приведения.
Ответ 11
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, которое будет отображаться со значением.
Ответ 12
Вот несколько статических методов, которые вы можете использовать для получения 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);
Вдохновленный ответом Микаэля Энгвера.
Ответ 13
для получения атрибута из перечисления, я использую:
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);
Ответ 14
Просто ищите подходящее место для размещения этого фрагмента кода.
скажем, у вас есть следующее свойство:
[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;
}