BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

Reflection
.NET 2.0+

Reflecting Attributes

The twenty-first part of the Reflection tutorial looks at the reflection of attributes. Attributes are used to decorate assemblies, types, members and other code to provide metadata about those items. With reflection, this metadata can be accessed.

Attributes

The .NET framework includes the concept of attributes. Attributes can be used to decorate elements of your code, such as assemblies, types, methods, properties and parameters. Attributes have various purposes. Some modify the behaviour of an element, such as the Serializable attribute, which declares that a type can be serialized. Others add information that is available at run-time or at design time, such as the DebuggerDisplay attribute, which includes a property that determines how a type is displayed in the Visual Studio debugger windows.

Attributes are simply instances of attribute classes that are stored within an assembly as metadata, rather than being part of the assembly's core code. Some of those attribute classes are very simple and require no additional properties. Serializable is such an attribute. Others include properties that modify the behaviour of the attribute. For example, the DebuggerDisplay attribute includes a string property that determines how the value will appear in the debugger. Some attributes use optional properties. For example, the Obsolete attribute, which marks an item as out of date, can be applied with or without an additional message.

The .NET framework includes a large number of attributes with many different purposes. In addition, you can create your own custom attributes. We will look at custom attributes in the final instalment of the Reflection tutorial. In this article we will concentrate on examining standard attributes using reflection. The techniques described here are identical to those used for reflecting custom attributes.

To demonstrate the reflection of attributes we'll use the following two classes. Person is used to hold a person's name and is decorated with two attributes. Employee is a subclass of Person that adds an extra property.

[DebuggerDisplay("{FirstName} {LastName}")]
[Serializable]
public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class Employee : Person
{
    public string Salary { get; set; }
}

As we are using reflection, include the following using directive in your project if you want to recreate the examples:

using System.Reflection;

Obtaining Attributes

When you have a type or a member and you wish to obtain all of its declared attributes, you can use the GetCustomAttributes method. This is defined in the MemberInfo class, so is available to many reflected items. In its simplest form, you call the method and pass false to its Boolean parameter. It then returns an object array containing the attributes.

In the following example we use the method against a Type instance representing the Person class, which has two attributes. We can see the types of the attributes in the outputted messages. The type names are shown because these attributes do not override the ToString method.

Type type = typeof(Person);
object[] attributes = type.GetCustomAttributes(false);
Console.WriteLine(attributes[0]);
Console.WriteLine(attributes[1]);

/* OUTPUT

System.Diagnostics.DebuggerDisplayAttribute
System.SerializableAttribute

*/

If you want to examine a specific attribute in detail, perhaps to obtain values from its properties, you must cast the returned object to its underlying type. In the code below, we cast the first item in the returned array as a DebuggerDisplayAttribute. We can then read the Value property, which holds the string used to generate values that would be displayed in Visual Studio's Locals and Autos windows, as well as in other areas of the debugger.

Type type = typeof(Person);
object[] attributes = type.GetCustomAttributes(false);
DebuggerDisplayAttribute attribute = (DebuggerDisplayAttribute)attributes[0];
Console.WriteLine(attribute.Value);

/* OUTPUT

{FirstName} {LastName}

*/

Filtering the Returned Attributes by Type

In many attribute reflection scenarios you will be looking for a specific attribute. Rather than obtain the array of all attributes and then extract the ones you want, you can supply the desired attribute type as the first parameter of the GetCustomAttributes method. The method still returns an object array but any attributes that are not of the specified type are filtered out of the results.

You can see this in the following code. Try stepping through the code and examining the results in the debugger. You will see that the SerializableAttribute object is not included this time.

Type type = typeof(Person);
object[] attributes = type.GetCustomAttributes(typeof(DebuggerDisplayAttribute), false);
DebuggerDisplayAttribute attribute = (DebuggerDisplayAttribute)attributes[0];
Console.WriteLine(attribute.Value);

/* OUTPUT

{FirstName} {LastName}

*/
17 August 2012