posts - 78, comments - 79, trackbacks - 84

My Links

News

View Anthony Trudeau's profile on LinkedIn

Add to Technorati Favorites

Article Categories

Archives

Post Categories

Other Links

Overview of custom attributes

Attributes are elements of metadata that you can attach to entities such as assemblies, classes, methods, properties, et al.  The .NET Framework makes use of many attributes that allow you to affect the behavior of the compiler or how the Framework uses an entity.  For example, you can assign attributes to properties of a control object that allow a friendly  name and description to be shown in the property browser in Visual Studio.

At some point, you'll come into a situation where you'll want to use a custom attribute.  Defining and using attributes are very easy and in this article I will provide a very simple setup using a few attributes defined at the class level.  Defining and using attributes at other levels vary only slightly and I'll make note when there is a difference.

The class we will use is a very simple class.  It has a default constructor, no methods, and no properties. 

 

    public class AttributedObject

    {

    }

 

The first thing that we need to do is define the attributes.  We're going to define two attributes.  The first attribute assigns a friendly name for the class.  And the second one will allow you to define any number of named options to the class.  The first class we'll define is for the name.

Every attribute class must inherit from System.Attribute and should be decorated by the AttributeUsageAttribute.  The AttributeUsageAttribute has special meaning to the compiler and will determine how you can use your custom attribute.  It has three basic properties: AttributeTargets, AllowMultiple, and Inherited.  The AttributeTargets property is a flag enumeration that specifies where the attribute can be used such as a class, method, or property.  Our attributes will only be allowed on class entities.  The AllowMultiple property specifies if the attribute can be defined more than once on an entity.  It wouldn't make much sense to have more than one name, but we will use this to allow a class to have more than one option.  The final property is Inherited.  It allows you to specify whether or not the attribute will apply to subclasses of the class its applied to.

Here's the definition of our name attribute.  It's important to note that the class name must end with Attribute -- no exceptions.  Notice also that we do not need to specify the Attribute portion of the name when we add the AttributeUsageAttribute to the NameAttribute class this is true of your custom attributes as well which you will see (you're welcome to add the Attribute suffix when you use the attribute if it makes you feel like you're doing more work).

 

    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]

    public class NameAttribute : Attribute

    {

        public NameAttribute(string name) :base()

        {

            Name = name;

        }

 

        public string Name { get; private set; }

    }

 

The AttributeUsageAttribute is the first place where things would be different if we were applying the attribute to methods, properties, or some other entity.  The AttributeTargets enumeration contains the different options.  The enumeration itself is decorated with the FlagsAttribute which allows the value to be a combination of the values of the enumeration.  For example, you could specify AttributeTargets.Class | AttributeTargets.Method and then the attribute could also be used on method signatures.

Applying this attribute to our simple class is trivial.  The attributes are enclosed within brackets just like they were when you used the AttributeUsageAttribute before.  Intellisense will show you what arguments you need when you type in the name you'll quickly realize that they correspond to the constructors you've defined in your attribute class.  Here's the class as it stands now.

 

    [Name("MyClass")]

    public class AttributedObject

    {

    }

 

The final attribute we need to define is our option class.  This attribute class is almost identical to the NameAttribute we created earlier.  The only difference is naming and a property value we specify with the AttributeUsageAttribute.  Remember we want to specify multiple options using this attribute.

 

   [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]

    public class OptionAttribute : Attribute

    {

        public OptionAttribute(string value)

            : base()

        {

            Value = value;

        }

 

        public string Value { get; private set; }

    }

 

That's it.  Setting AllowMultiple to true will allow us to add this attribute any number of times to our simple class.  The simple class can then be modified with three separate options as follows:

 

    [Name("MyClass")]

    [Option("1"), Option("2"), Option("3")]

    public class AttributedObject

    {

    }

 

I've added the OptionAttribute decoration on a separate line, but that is not a requirement.  They can be all in one long comma-separated list that can span multiple lines in C#.

The next step is actually using the attributes in your code.  Afterall, you didn't put the attributes on your classes for fun (although you could use them for some type of documentation purpose).  The Attribute class has a handful of static methods for using your custom attributes.  Using these methods is the other place where the target of the attribute is important.  The first argument to these methods essentially defines the target.  For a class, you pass the type, but for other entities you need to specify a different reflection based object such as a MemberInfo object for a method or property.

The first method we'll talk about is IsDefined.  This method determines if the entity is decorated with a specific attribute.  Here's how we could use it in our situation:

 

bool found = Attribute.IsDefined(typeof(AttributedObject), typeof(NameAttribute));

 

The next method we would use for attributes that can only be defined once (i.e. our NameAttribute). The method is GetCustomAttribute  An exception will be raised if you use this method for an attribute that is defined multiple times.  Once I get the attribute object I can look at its public members like any other object instance.

 

NameAttribute name = (NameAttribute)Attribute.GetCustomAttribute(typeof(AttributedObject), typeof(NameAttribute));

Console.WriteLine("Name for {0} is {1}", typeof(AttributedObject).Name, name.Name);

 

The final method that I'm covering is the GetCustomAttributes method.  This method is what we'll use for attributes that can be added to an entity multiple times.  In this case, that means our OptionAttribute.  In this example I also illustrate how you can use LINQ to query the list.  My example is a little to simple to justify using LINQ, but I aim to just illustrate it can be done.  In simple circumstances like this you could just cast the Attribute[] to an array of your attribute type and then use a foreach over the results.

 

var optionQuery = from option in Attribute.GetCustomAttributes(typeof(AttributedObject), typeof(OptionAttribute))

                  select option;

 

Console.WriteLine("Options on {0}", typeof(AttributedObject).Name);

foreach (OptionAttribute option in optionQuery)

{

    Console.WriteLine("Value = {0}", option.Value);

}

 

I hope this overview has been helpful for you.  Once you start working with custom attributes you'll find that they can be very useful in providing behavior in your API with a minimal amount of coupling.  Undoubtedly, you'll also find other uses for the attributes and I welcome any comments on how you use attributes.

Print | posted on Wednesday, January 16, 2008 9:49 AM |

Feedback

Gravatar

# re: Overview of custom attributes

Hi,

Thanks for the good post... I would like to do exactly this to give display names to my class properties. Problem is that my web app is multilingual and I would like to pass in the display name to the attribute constructor from a resource file. When I try this it says that a constant expression is required.

Is there any way I can achieve this using custom attributes and resource files?

Many thanks,

Rob.
4/23/2008 8:03 PM | Rob
Gravatar

# re: Overview of custom attributes

As you found out you cannot use any instance defined value within an attribute. So using an attribute value for some display text kind of depends on where you are trying to display the text. I wrote an article awhile ago for localizing the properties for use in the property grid (http://geekswithblogs.net/tonyt/articles/70280.aspx), but if you have control over what is doing the displaying then you can always just specify the resource name and then have a method on the attribute that takes a resource manager instance as an argument.
4/24/2008 12:38 AM | Anthony Trudeau

Post Comment

Title  
Name  
Email
Url
Comment   
Please add 1 and 2 and type the answer here:

Powered by: