3

I'm building my own library for attributes, and I want to check if there is an attribute on property level. Currently I have this method that works just fine:

    public static bool HasPropertyAttribute<T>(this object instance, string propertyName)
    {
        return Attribute.GetCustomAttributes(instance.GetType().GetProperty(propertyName), typeof(T), true).Any();
    }

Now I'm looking into solution that will allow me to pass lambda expression instead of string as propertyName. Is there an elegant way to do it without adding this method to become two generic types dependant, aka:

HasPropertyAttribute<T, TProperty>(...).
Admir Tuzović
  • 10,997
  • 7
  • 35
  • 71

2 Answers2

6

You can use Lambda Expressions to resolve a compile-time property reference. (code modified from Retrieving Property name from lambda expression)

public PropertyInfo GetPropertyInfo<TProperty>(
    Expression<Func<TProperty>> propertyLambda)
{
    MemberExpression member = propertyLambda.Body as MemberExpression;
    if (member == null)
        throw new ArgumentException(string.Format(
            "Expression '{0}' refers to a method, not a property.",
            propertyLambda.ToString()));

    PropertyInfo propInfo = member.Member as PropertyInfo;
    if (propInfo == null)
        throw new ArgumentException(string.Format(
            "Expression '{0}' refers to a field, not a property.",
            propertyLambda.ToString()));

    return propInfo;
}

You don't need to use it as an extension method (though if you want to adapt it you can, but having a source object instance is not necessary except to write the line)

public class Test
{
    public string Prop { get; set; }
}

Test t = new Test();
PropertyInfo propInfo = GetPropertyInfo(() => t.Prop);
Console.WriteLine(propInfo.Name + " -> " + propInfo.PropertyType); //Prop -> System.String

EDIT: If you want to have some nice syntax and have to avoid having an existing reference to an object of the type, you can do something like:

public static class TypedReflection<TSource>
{
    public static PropertyInfo GetPropertyInfo<TProperty>(
        Expression<Func<TSource, TProperty>> propertyLambda)
    {
        MemberExpression member = propertyLambda.Body as MemberExpression;
        if (member == null)
            throw new ArgumentException(string.Format(
                "Expression '{0}' refers to a method, not a property.",
                propertyLambda.ToString()));

        PropertyInfo propInfo = member.Member as PropertyInfo;
        if (propInfo == null)
            throw new ArgumentException(string.Format(
                "Expression '{0}' refers to a field, not a property.",
                propertyLambda.ToString()));

        return propInfo;
    }
}

And call it like:

PropertyInfo propInfo = TypedReflection<Test>.GetPropertyInfo(o => o.Prop);

At this point, it's pretty easy to add additional typed reflection methods (get methods, fields, etc.)

EDIT: It's still sorta two generic types dependent, but it's hidden away through type inference. I prefer the second example; at the very minimum you need to specify the declaring class type (because you want type safety), but you don't need an object instance. It also has the benefit (which I suspect you're after) that if you rename the property, it's propagated to this code obtaining the PropertyInfo.

Community
  • 1
  • 1
Chris Sinclair
  • 22,858
  • 3
  • 52
  • 93
0

I've opted for this solution, least code of all:

    public static bool HasPropertyAttribute<T, TProperty>(this T instance, Expression<Func<T, TProperty>> propertySelector, Type attribute)
    {
        return Attribute.GetCustomAttributes(instance.GetType().GetProperty((propertySelector.Body as MemberExpression).Member.Name), attribute, true).Any();
    }

Gets invoked like this:

var cc = new CustomClass();
cc.HasPropertyAttribute(x => x.Name, typeof(NullableAttribute))
Admir Tuzović
  • 10,997
  • 7
  • 35
  • 71