New in PostSharp 2.1: Reflecting Custom Attributes

by Gael Fraiteur on 21 Jul 2011

One of the breaking innovations of PostSharp 1.0 in 2005 was the ability to execute aspect code at build time. Whereas other AOP frameworks developed complex declarative syntaxes to express “pointcuts”, PostSharp just allowed you to use System.Reflection to select the target of aspects. LINQ made it even easier to express complex code queries.

Yet, some queries were difficult to achieve just with System.Reflection. For instance, it is fairly difficult and inefficient to get all properties annotated with the [DataMember] custom attribute: you have to enumerate all types (and do a recursion on nested types), get all properties of these types, and check the presence of this custom attribute on all properties. Wow. If we were querying a relational database, we would probably create an index on the Type column of the CustomAttribute table. Well, the analogy is not so stupid. Metadata in a .NET assembly is in fact a relational database, and there is a CustomAttribute table. What is more, PostSharp already builds these indexes internally. So why not making them available to user code? This was the principal idea behind this new feature: reflection search.

Reflection Search is simply this: a set of methods making it easier and more efficient to query metadata of the assembly being processed by PostSharp. Therefore, this API is available only at build time.  It is not intended to “improve” System.Reflection for run-time use. That said, if you need to do some complex reflection query at runtime, it’s better to do it at build time using PostSharp, serialize the result and store it as a managed resource.

The API is exposed in the class PostSharp.Reflection.ReflectionSearch. Note that the feature is available in the Professional Edition only.

Enumerating Custom Attributes

Custom attributes of a type

The first set of methods you will find on the ReflectionSearch class allow you to enumerate custom attributes. GetCustomAttributesOnType retrieve all custom attributes of a given type in the current assembly (remember that this works only at build time, so the notion of “current assembly” refers to the assembly being processed by PostSharp). The second overload takes an additional parameter ReflectionSearchOptions. If you specify the value IncludeDerivedTypes, you will get all custom attributes of the specified type or any type derived from it.

There is no example in the system class library to illustrate a hierarchy of custom attributes, so let's create our own:

// A hierarchy of custom attributes.

class FruitAttribute : Attribute {}

class AppleAttribute  : FruitAttribute {}

class OrangeAttribute : FruitAttribute {}

class VegetableAttribute : Attribute {}

class TomatoAttribute : VegetableAttribute {}

// Using the custom attributes

[Fruit]
class Team
{
   [Apple]
   Dude tom;

   [Orange]  
   Dude jerry;

   [Tomato]
   Dude donald;

}

// Enumerating the custom attributes (build-time)
class MyAspect : AssemblyLevelAspect, IAspectProvider
{
 
    public IEnumerable ProvideAspects(object target)
    {

      CustomAttributeInstances[] attributes = ReflectionSearch.GetCustomAttributesOfType( 
typeof(FruitAttribute), ReflectionSearchOptions.IncludeDerivedTypes ); // TODO: Do something with that. } }

 

The method GetCustomAttributesType will return three instances of the custom attribute: (FruitAttribute, Team), (AppleAttribute ,Team.tom) and (OrangeAttribute, Team.jerry).

If you wonder why the method does not return a tomato: it is by decision of the U.S. Supreme Court, which, on May 10, 1893, declared that tomatoes are vegetables because – although they are classified as fruits in botany –  they are usually served with dinner and not dessert. As far as I am concerned, I still don’t eat tomatoes for dessert, the only thing that changed is that we are now much more accustomed to bureaucracy (and today’s tomatoes have better resistance to pressure because the species were selected to accommodate mechanical picking, and long storage and transportation – so arguably we now eat more tomatoes than in the XIXth century, but they are probably less tasty).

Anyway.

Note that the method returns a CustomAttributeInstance, which includes information about the custom attribute and the element of code to which it is applied.

Multicast attributes

Keep in mind that ReflectionSearch reflects the internal repository of custom attributes. This repository does not only include custom attributes that you add to your code manually. It also includes all “pseudo” custom attributes added by different components of PostSharp, starting with multicast attributes (remember that aspect class typically derive from MulticastAttribute).

When you use multicast custom attributes in your code, PostSharp executes the multicasting algorithm to populate the internal repository. The repository then contains the concrete instances of the custom attribute.

Let’s take an example:

[MulticastAttributeUsage( MulticastTargets.Method )]
class FooAttribute : MulticastAttribute {}

[Foo]
class MyClass
{
   void MyMethod() {}
}

In that case the repository contains an instance of FooAttribute on MyMethod but not on MyClass. The normal reflection API would give the opposite result (at least when it is run inside PostSharp or before PostSharp), because it is not aware of multicast semantics.

Since you can’t use normal reflection to retrieve multicast custom attributes, ReflectionSearch offers a second method: GetCustomAttributesOnTarget. As the name suggests, it returns the list of all custom attributes on a given element of code.

Summary

The high-level library PostSharp.dll now offers access to the internal repository of custom attributes. Since this repository, and all indexes, are used by PostSharp itself, using this new feature has minimal impact on build time. Now, you have a way to query custom attributes by type without killing performance. And you are finally able to query attributes that were created by PostSharp as the result of the multicasting algorithm.

But that’s not all ReflectionSearch has to offer. Next in line: browse relationships between elements of code. But this is for another day.

Happy PostSharping!

-gael