3

I use the following code to get attributes from DataAnnotations:

public static T GetAttributeFrom<T>(this object instance, string propertyName) where T : Attribute
{
    var attrType = typeof(T);
    var property = instance.GetType().GetProperty(propertyName);
    return (T)property .GetCustomAttributes(attrType, false).First();
}

Thanks to user: jgauffin

I want to improve his solution by pass a lambda expression instead a string:

public static T GetAttributeFrom<T>(this object instance, Expression<Func<T>> propertyExpression) where T : Attribute
{
    var attrType = typeof(T);
    var property = instance.GetType().GetProperty(GetPropertyNameFromLambdaExpression(propertyExpression));
    return (T)property.GetCustomAttributes(attrType, false).First();
}

and my method to get the real property name from this expression:

public static string GetPropertyNameFromLambdaExpression<T>(Expression<Func<T>> propertyAsExpression)
{
    var memberExpression = propertyAsExpression.Body as MemberExpression;
    var propertyInfo = memberExpression.Member as PropertyInfo;
    if (memberExpression != null && propertyInfo != null)
    {
        return memberExpression.Member.Name;
    }
    throw new ArgumentException(propertyAsExpression.ToString());
}

If I try something like this:

var id = this.GetAttributeFrom<RangeAttribute>(()=> Id).Maximum;

I get this error:

Cannot convert expression type 'int' to return type 'RangeAttribute'

How can I fix this?

Community
  • 1
  • 1
GrayFox
  • 997
  • 1
  • 9
  • 26

1 Answers1

4

You are using the generic type T as a type of the attribute and the property at the same time. Try to start with:

public static T GetAttributeFrom<T>(this object instance, string propertyName) where T : Attribute {
    var attrType = typeof(T);
    var property = instance.GetType().GetProperty(propertyName);
    return (T)property.GetCustomAttributes(attrType, false).First();
}

public static T GetAttributeFrom<T, U>(this object instance, Expression<Func<U>> propertyExpression) where T : Attribute {
    var attrType = typeof(T);
    var property = instance.GetType().GetProperty(GetPropertyNameFromLambdaExpression<U>(propertyExpression));
    return (T)property.GetCustomAttributes(attrType, false).First();
}

public static string GetPropertyNameFromLambdaExpression<U>(Expression<Func<U>> propertyAsExpression) {
    var memberExpression = propertyAsExpression.Body as MemberExpression;
    var propertyInfo = memberExpression.Member as PropertyInfo;
    if (memberExpression != null && propertyInfo != null) {
        return memberExpression.Member.Name;
    }
    throw new ArgumentException(propertyAsExpression.ToString());
}

then you can call this in this way:

var max = this.GetAttributeFrom<RangeAttribute, int>(() => Id).Maximum;

You can't ommit the second type (GetAttributeFrom<RangeAttribute>(() => Id)): C# generics can't infer second parameter?.

Community
  • 1
  • 1
romanoza
  • 4,775
  • 3
  • 27
  • 44
  • Yes, i realized that eventually. It'll work only *within* the class itself. `this.Id` could have been useful in this case. Nice solution. – haim770 Jan 31 '16 at 08:38