Click here to Skip to main content
Email Password   helpLost your password?

Attribute programming has a lot of benefits and, when done correctly, can greatly simplify the amount of code that you need to write. One drawback to using attributes is that the code required to retrieve a custom attribute from a type is a bit cumbersome and is very repetitious.

Given a type, the simplest way to retrieve a custom attribute is code like

CustomAttribute attribute = Attribute.GetCustomAttribute(customType.GetType(),
    typeof(CustomAttribute), true) as CustomAttribute;

While this is simple code, it doesn’t handle any error conditions and requires that you always remember to perform the cast. A more complete method would look like

public static CustomAttribute GetAttribute(MemberInfo element)
{
    CustomAttribute attribute = null;

    try
    {
        attribute = Attribute.GetCustomAttribute(element, typeof(CustomAttribute),
            true) as CustomAttribute;
    }
    catch
    {
        // We aren't really interested in the exceptions here, but if we do
        // get an exception just return null;
        attribute = null;
    }

    return attribute;
}

This nicely encapsulates the error handling and casting, but introduces another drawback. In order to make use of this method you would need to include it on every custom attribute you create, being sure to change the types appropriately.

We can make this more practical by changing to a generic extension method with very little effort

public static T GetAttribute<T>(this MemberInfo element) where T: Attribute
{
    T attribute = null;

    if (element != null)
    {
        try
        {
            attribute = Attribute.GetCustomAttribute(element, typeof(T), true) as T;
        }
        catch
        {
            // We aren't really interested in the exceptions here, but if we do
            // get an exception just return null;
            attribute = null;
        }
    }

    return attribute;
}

The benefit here is that, because this is implemented as an extension method it is available as if it were a real method call on any class derived from MemberInfo, which happens to be the base class for all of the Type classes.

Now, we can define our custom attributes without any special consideration to providing a strongly typed GetAttribute method and when we want to retrieve a custom attribute, we can use code that now looks like

CustomAttribute attribute = customType.GetType().GetAttribute<CustomAttribute>();

It might not look like a major change in the calling site, but we are now able to quickly and easily get a strongly typed attribute given an instance type.

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralA variation on this theme that handles multiple attributes and caches results ...
HightechRider
13:07 8 Jun '09  
Reflection can be slow, so if you are using this a lot you might want to cache the results. Also, it's possible that a class might have multiple attributes applied to it derived from the same base attribute type and you might want all of them. And finally, depending on how you use this, GetType() can move into the method simplifying things further.

Here's how I do it (for comparison) ...

private static Dictionary<Type, object[]> attributeCache = new Dictionary<Type, object[]>();

public static IEnumerable<T> GetAttributes<T>(this object obj) where T : Attribute
{
lock (attributeCache)
{
object[] attrs;
if (!attributeCache.ContainsKey(typeof(T)))
{
attrs = obj.GetType().GetCustomAttributes(false);
attributeCache.Add(typeof(T), attrs);
}
else
{
attrs = attributeCache[typeof(T)];
}
return attrs.OfType<T>();
}
}


Note: I also cache all the custom attributes on the first call assuming that if you ask for one you'll probably come back for the rest.
GeneralRe: A variation on this theme that handles multiple attributes and caches results ...
Scott Dorman
4:23 9 Jun '09  
All valid points. Yes, reflection does have a cost and caching the results will minimize that cost but at a slightly higher memory footprint. Having multiple attributes is also most likely but in my experience I am typically interested in only one at any given time.

Your version of GetAttributes assumes that I'm more interested in being able to enumerate over the collection of attributes returned (even if there is only one) while most of the time I'm more interested in just the one attribute that I know I need and working with it's properties directly.

I did not want to push the call to GetType in to the method for a few reasons. First, extension methods shouldn't be defined on object as not all languages will understand that. Second, by putting it on MemberInfo I allow this call to be made from any number of derived types (EventInfo, FieldInfo, MethodBase, PropertyInfo, and Type) which actually gives me more flexibility from the calling site.

Scott Dorman
Microsoft® MVP - Visual C# | MCPD
President - Tampa Bay IASA

[Blog][Articles][Forum Guidelines]
Hey, hey, hey. Don't be mean. We don't have to be mean because, remember, no matter where you go, there you are. - Buckaroo Banzai

GeneralRe: A variation on this theme that handles multiple attributes and caches results ...
HightechRider
9:01 9 Jun '09  
All good points and yes, few seem to realize that memory is actually *far* more precious than CPU cycles these days. I've lost count of how many ill-conceived caching attempts I've seen where folks gain microseconds in one area but cause slow downs in other areas as their cache consumes on-chip and off-chip RAM that could have saved a disc or network access instead.

But typically I see attribute access patterns happening a lot of times on a very small set of objects (often just a handful of types) which makes them an ideal candidate for caching.

Readers can decide which approach to use, or blend the two based on their own requirements and expected usage patterns.


Last Updated 8 Jun 2009 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010