Getting started with events – part IV

Now that we already know how to create new custom attributes and how to restrict its use, it’s time to wrap up this series with a final post that shows how to rely on custom attributes to change the behavior of your code at runtime. One of the first things you’ll need to do (if not the first!) is check for the existence of an attribute. If you have a reference to a type (or can get a reference to a type), then you can perform this check by using the IsDefined method. Suppose we’ve got the following code:

classMyAttribute:Attribute{}

internalclassStudent {

    [My]

    publicString Nome { get; set; }

    publicString Morada { get; set; }

}

And now, suppose we need to see if the MyAttribute is applied to the Student type. Performing that check is not hard, as you can see from the following snippet:

var st = newStudent( );

Console.WriteLine( st.GetType(  )

                        .IsDefined( typeof(MyAttribute), true) );

Console.WriteLine( typeof( Student ).IsDefined( typeof( MyAttribute ), true );

Both options return the same result (false in both cases). Btw, the second parameter passed to IsDefined is a Boolean which specifies if the attribute should be searched in the inheritance chain. Lets suppose you want to check the if the attribute is applied to a property of the Student class…once again, you can rely on the IsDefined method, but this time, you’ll have to go through each property’s type:

var props = typeof( Student ).GetProperties( )

    .Where( p => p.IsDefined( typeof( MyAttribute ), true ) );

foreach( var propertyInfo in props ) { //prints Name only

    Console.WriteLine(propertyInfo.Name);

}

As you can see, the previous code is really similar to the one we had before: the difference is that in this case we’re resorting to LINQ expression to filter the properties to which the attribute has been applied.

There are, however, other interesting options for checking for custom attributes. For instance, the Attribute class introduces several overloads of the following methods (btw, there are several derived Type classes which also expose similar members – we’ve already met the one defined by the PropertyInfo type in the previous snippet):

  • IsDefined: returns true when an instance of the specified custom attribute is associated with the target;
  • GetCustomAttributes: returns an array where each element is an instance of the specified custom attribute that was applied to the target;
  • GetCustomAttribute: returns an instance of the specified attribute if it was applied to the target.

If you’re looking for performance and you’re only interested in checking if a custom attribute was applied to a specific target, then you should use the IsDefined method. This is the most efficient method of the previous list because it doesn’t construct any instance of the custom attribute class. Unfortunately, there are times where you do need to get info about the values of the properties of an attribute class and, in these cases, there’s really no option: you do need to use one of the other methods presented in the previous list. Btw, in these scenarios it’s also a good idea to perform some sort of caching because checking for a specific custom attribute and deserializing it from the metadata tables is not an operation you’d want to perform often.

Before moving on, there’s an interesting gotcha associated with the use of the methods shown in the previous list. The next snippet illustrates this behavior:

classMyAttribute:Attribute{}

classMyDerivedAttribute: MyAttribute{}

classStudent {

    [MyDerived]

    publicString Nome { get; set; }

    publicString Morada { get; set; }

}

And now, what happens if we run the previous property check (which looks for the custom attribute MyAttribute)? It will still return the Name property bacause it is annotated with the MyDerivedAttribute attribute. This might not be problematic, but it’s certainly not what you want if, for some reason, you need to ensure that the type matches only MyAttribute instances (and not its derived types). To get this behavior, you can use something like this:

var props = typeof( Student ).GetProperties( )

    .Where( p => Attribute.GetCustomAttributes( p ).Any( att => att.GetType( ) == typeof( MyAttribute ) ) );

Until now, I’ve only written code which tests for the existence of a specific custom attribute. Sometimes, you need to check if a specific custom attribute has its properties initialized to some values. There are several options here:

  • you can get an instance of the custom attribute and then check for its properties values;
  • you create a new instance of the attribute class with the “correct” values and rely on the Equals method which the base Attribute class overrides. Internally, the method will check the type of the attributes and its fields (when both attributes have the same type). Since this comparison relies in reflection, you can improve the performance of the code by overriding this method in your custom attributes so that it doesn’t rely on reflection;
  • Besides Equals, there’s also a Match method which you can override for performing more “intelligent” comparisons.

But there’s still more…if you look at the MSDN docs, you’ll find an interestingly named class: CustomAttributeData. If you take a peek at the docs, you’ll notice that it introduces several overloads of the GetCustomAttributes method. In practice, you’ll use this class (and one of its GetCustomAttributes’ overload method) when you need to check for attributes for a type whose assembly was loaded through the Assembly’s ReflectionOnlyLoad method (when you load an assembly through this method, the CLR won’t execute any code in it). In these cases, you do need to rely on this helper class for getting info about its metadata since there’s no way for the code in that assembly to get executed. As you might expect, there are some differences associated with the use of this class…

For starters, the method returns a list of instance of the CustomAttributeData type (one per attribute applied to the “current” target). After getting this list, we can query each instance to determine how the object would be instantiated (ie, check which constructor would be called through the Constructor property) and see which arguments would be initialized (through the property ConstructorArguments). As you’d expect, the approach is slightly different from the one used before. To show a possible  use of this class, lets change the previous code for getting the properties of a type which have been annotated with the MyAttribute attribute:

var props = typeof( Student ).GetProperties( )

    .Where( prop => CustomAttributeData.GetCustomAttributes( prop )

                .Any( p => p.Constructor.DeclaringType == typeof(MyAttribute)) );

As you can see, p doesn’t reference an attribute instance but a CustomAttributeData instance. As we’ve said, this means that we need to go through the Constructor.DeclaringType property to get the type of the attribute (which was the only thing we need to do in this scenario).

And I’d say that’s it for now. Stay tuned for more.

Advertisements

~ by Luis Abreu on July 18, 2011.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: