Как получить аттрибут свойства типа с помощью лямбда выражения?
Имеется расширениe для типа, которое используется для возврата атрибутов свойств:
public static T GetAttribute<T>(this Type type, string propertyName) where T : Attribute
{
return type
.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance)
?.GetCustomAttributes(typeof(T), false)
.Cast<T>().FirstOrDefault();
}
var displayNameAttribute = typeof(Person)
.GetAttribute<DisplayNameAttribute>(nameof(Person.Id));
Недостаток в том, что имя свойства передаётся как текст и не связано с типом.
Можно ли каким-либо образом преобразовать это расширение для использования с лямбда выражением?
Хотелось бы использовать расширение таким образом:
var displayNameAttribute = typeof(Person)
.GetAttribute<DisplayNameAttribute>(p => p.Id);
На данный момент всё что я смог сделать (и то с помощью @AlexanderPetrov):
public static class TypeUtils
{
public static TAttr GetAttribute<TAttr, TEntity>(
Expression<Func<TEntity, object>> argumentFunc)
where TAttr : Attribute
{
string propertyName = GetMemberInfo(argumentFunc).Member.Name;
return typeof(TEntity)
.GetProperty(propertyName,
BindingFlags.Public | BindingFlags.Instance)
?.GetCustomAttributes(typeof(TAttr), false)
.Cast<TAttr>().FirstOrDefault();
}
private static MemberExpression GetMemberInfo(Expression method)
{
if (method is not LambdaExpression lambda)
{
throw new ArgumentNullException(nameof(method));
}
MemberExpression memberExpr = null;
if (lambda.Body.NodeType == ExpressionType.Convert)
{
memberExpr = ((UnaryExpression)lambda.Body)
.Operand as MemberExpression;
}
else if (lambda.Body.NodeType == ExpressionType.MemberAccess)
{
memberExpr = lambda.Body as MemberExpression;
}
return memberExpr
?? throw new ArgumentException(null, nameof(method));
}
}
// используется так
var attr = TypeUtils.GetAttribute<DisplayNameAttribute, Person>(p => p.Id);
Но у меня не получается сделать это именно как расширение для типа.
Ответы (1 шт):
public static T GetAttribute<T, T2>(this Type type, Expression<Func<T2>> func)
where T : Attribute
{
var expressionBody = (MemberExpression)func.Body;
string propertyName = expressionBody.Member.Name;
return type
.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance)
?.GetCustomAttributes(typeof(T), false)
.Cast<T>().FirstOrDefault();
}
Использование:
var p = new Person();
var displayNameAttribute = typeof(Person)
.GetAttribute<DisplayNameAttribute, int>(() => p.Id);
Console.WriteLine(displayNameAttribute.DisplayName);
Недостаток в том, что нужно явно указывать тип свойства в вызове метода. В данном случае указан int.
Можно объявить метод так:
public static T GetAttribute<T, T2, T3>(this Type type, Expression<Func<T2, T3>> func)
В этом случае становится не нужен экземпляр var p = new Person();.
Но дополнительно придётся указать тип в вызове метода:
var displayNameAttribute = typeof(Person)
.GetAttribute<DisplayNameAttribute, Person, int>(p => p.Id);