RK.
RK.

Reputation: 197

Accessing attributes on fields using extension method

I want to check custom attributes on my class members (fields only) by using an extension method.

public class DatabaseIdAttribute : Attribute
{
    public int ID { get; set; }

    public DatabaseIdAttribute(int id)
    {
        this.ID = id;
    }
}

public class MyClass 
{
    [DatabaseId(1)]
    double Height {get;set;}

    [DatabaseId(2)]
    double Width {get;set;}

    double Area { get { return this.Height * this.Width; }
}

I want to use LINQ expression in the extension method to access the class field instead of passing magic strings.

var myClass = new MyClass();
var attribute = myClass.GetAttribute<DatabaseIdAttribute>(c => c.Height);

Is it possible to achieve?

[EDIT]

For the time being, I have achieved the following with the help of @leppie

    public static MemberInfo GetMember<T, R>(this T instance, Expression<Func<T, R>> selector)
    {
        var member = selector.Body as MemberExpression;
        if (member != null)
        {
            return member.Member;
        }
        return null;
    }

    public static T GetAttribute<T>(this MemberInfo member) where T : Attribute
    {
        return member.GetCustomAttributes(false).OfType<T>().SingleOrDefault();
    }

which enables to get the attribute in the following way

var c = new MyClass();
var attribute = c.GetMember(m => m.Height).GetAttribute<DatabaseIdAttribute>();

but I want to be able to access it in the following way

var c = new MyClass();
var attribute = c.GetAttribute<DatabaseIdAttribute>(m => m.Height);

Upvotes: 3

Views: 2918

Answers (2)

leppie
leppie

Reputation: 117220

You are almost there! This should work (untested).

public static class ObjectExtensions
{
    public static MemberInfo GetMember<T,R>(this T instance, 
         Expression<Func<T, R>> selector)
    {
        var member = selector.Body as MemberExpression;
        if (member != null)
        {
            return member.Member;
        }
        return null;
    }

    // unnecessary in .NET 4.5 and up, see note!
    public static T GetAttribute<T>(this MemberInfo meminfo) where T : Attribute
    {
       return meminfo.GetCustomAttributes(typeof(T)).FirstOrDefault() as T;
    }
}

Usage:

var attr = someobject.GetMember(x => x.Height).
              GetAttribute<DatabaseIdAttribute>();

Note: As of .NET 4.5 and up (including .NET Core), the BCL provides a GetCustomAttribute<T>(MemberInfo) extension method that functions identically to the GetAttribute method defined above, and should be used instead if available.

Upvotes: 8

DaddySharkAU
DaddySharkAU

Reputation: 71

If you don't mind supplying the extra generic types, you can do this:

public static class ReflectionHelper
{
    public static TAttr GetAttribute<TClass, TProp, TAttr>(Expression<Func<TClass, TProp>> selector) where TAttr : Attribute
    {
        var member = selector.Body as MemberExpression;
        return member.Member.GetCustomAttributes<TAttr>(false).First();
    }
}

Then you can use it like this:

var attribute = ReflectionHelper.GetAttribute<MyClass, double, DatabaseIdAttribute>(m => m.Height);

Note that this is not an extension method (as we can't have static extensions) so there is no need to create an instance of your class.

Of course you could still have the extension method version:

public static TAttr GetAttribute<TClass, TProp, TAttr>(this TClass instance, Expression<Func<TClass, TProp>> selector) where TAttr : Attribute
{
   var member = selector.Body as MemberExpression;
   return member.Member.GetCustomAttributes<TAttr>(false).First();
}

Which would let you call it like so:

var c = new MyClass();
var attribute = c.GetAttribute<MyClass, double, DatabaseIdAttribute>(m => m.Height);

But either way is quite verbose. If you want the compile to infer all of the generic types, we'd need to pass in an instance of our attribute:

public static class ReflectionHelper
{
    public static TAttr GetAttribute<TClass, TProp, TAttr>(TAttr attribute, Expression<Func<TClass, TProp>> selector) where TAttr : Attribute
    {
        var member = selector.Body as MemberExpression;
        return member.Member.GetCustomAttributes<TAttr>(false).First();
    }
}

Usage:

var attribute = ReflectionHelper.GetAttribute(new DatabaseIdAttribute(), m => m.Height);

Upvotes: 1

Related Questions