Attributes of C#

This article was originally published in VSJ, which is now part of Developer Fusion.
C# has supported and usefully employed attributes for some time, but there is still a great deal of confusion about when custom attributes might or might not be useful. They also seem to be mysterious and advanced when they are used in advanced topics such as P/invoke and COM interop. After recently encountering some very strange uses of attributes it seems a good time to explain just how accessible they really are.

Comments for the run time

An attribute is an item of data associated with a programming construct that is stored in the assembly and which can be accessed by the use of reflection. Notice that some confusion is caused by the fact that some built-in attributes also additionally affect the way that the compiler seems to work. In the main this is not something that most attributes, and certainly not custom attributes, do. Essentially a custom attribute is a comment that gets compiled into your code, as metadata, and which other code, including you own, can retrieve and possibly act on. This is the standard explanation of an attribute but the attribute mechanism is so much more. When you retrieve an attribute an instance of a class that represents the attribute is created. Understanding how this mechanism works means that you can think about exactly how attributes might be useful and how things can go wrong.

There is a sense in which an attribute can always be replaced by a property. For example, you might mark a class as using ASCII encoding for its text and hence implement an [ASCII] attribute. Any other class that make use of a class so marked would have to inspect it to discover that it was ASCII and then arrange to treat it correctly. It should be fairly obvious that an alternative is to use an ASCII property set to True or False to indicate the same information. Notice that the two approaches have some similarities and some differences:

  1. Any client that uses the class has to implement retrieving and testing the attribute or the property and act accordingly – that is, nothing is enforced.
  2. A property can be set at compile time and can be changed at run time. An attribute on the other hand is set at compile time and generally isn’t changed at runtime.
  3. A property is associated with instances of a class but an attribute can be associated with many different entities including classes, methods, parameters and so on.
So “attributes don’t change and can be associated with a range of entities” seems to sum up the situation. Let’s see how this works.

Implementing a custom attribute

Implementing a custom attribute such as [ASCII] is fairly easy as long as you don’t read into it more than there is. All you have to do is create a class that inherits from Attribute:
public class ASCIIattribute : Attribute
{ }
It is usual to use a name of the form Xattribute, and if you do the system will treat the names X and Xattribute as the same. Unfortunately if you do declare the class as Xattribute, e.g. ASCIIattribute, then Intellisense prompts with the full name. Personally I’d rather keep attribute names short and meaningful so I prefer:
public class ASCII : Attribute
{ }
Now you can apply the attribute to any class as in:
[ASCII]
public class MyClass
{ }
Of course nothing happens if you do this but it is legal syntax – now to give it some semantics.

The key to understanding what is going on here is to realise that the attribute class is instantiated when anything queries MyClass’s custom attributes. To query custom attributes we use the GetCustomAttributes static method of the Attribute class:

Attribute.GetCustomAttributes(t)
This returns an array consisting of an instance of each attribute class corresponding to the attributes that have been applied to the specified type t. For example, the instruction:
Attribute[] attrs =Attribute.
	GetCustomAttributes(typeof(MyClass));
…returns an array with a single element attrs[0] which contains an instance of ASCII.

This way of retrieving attributes is the one you most often encounter in examples of attribute use. However, in many situations there is an alternative which is arguably better. In most cases you don’t really want to retrieve all the attributes applied to an entity because you are writing code which is going to handle just one attribute type – i.e. the custom attribute you are currently implementing. There is a fairly easy way to retrieve attributes by type from the type to which they are applied using the GetCustomAttribute method of the Type object to return attributes of a specified type. For example, to retrieve any ASCII attributes applied to MyClass you would use:

object[] attrs=typeof(MyClass).
	GetCustomAttributes(
	typeof(ASCII), false);
This returns an array of objects that can be cast to the attribute type you are looking for. If you want to make things easier you can perform the cast on the returned array:
ASCII[] attrs=(ASCII[]) typeof(MyClass).
	GetCustomAttributes(
	typeof(ASCII), false);
This always works because only attributes of ASCII type will be returned or a null reference which can also be cast to ASCII.

So far so good, but currently the instance of the attribute’s class isn’t doing us a lot of good as the class has no methods and, more importantly, no properties. There are two ways of providing it with properties. The first is to provide a constructor with parameters. For example if we change the ASCII class definition to:

public class ASCII : Attribute
{
	public string MyData;
	public ASCII(string MyDataIn)
	{ MyData = MyDataIn; }
}
…we can now pass some data to the class by applying the attribute as:
[ASCII("Hello Attribute World") ]
public class MyClass
{ }
Now when the GetCustomAttributes method is called it returns a single instance of the ASCII class with MyData set to “Hello Attribute World”. In other words, calling the GetCustomAttributes method has resulted in the class constructor being used with the data provided by the ASCII attribute tag. The tag is a call to the constructor and you can think of:
[ASCII("Hello Attribute World") ]
…as being equivalent to
attrs[0]=new ASCII(
	"Hello Attribute World");
…being executed when you call GetCustomAttributes on any class that the attribute is applied to. To see that you have indeed got an instance of the ASCII class complete with the string set by the constructor try:
ASCII[] attrs=(ASCII[]) typeof(MyClass).
	GetCustomAttributes(
	typeof(ASCII), false);
MessageBox.Show(attrs[0].MyData);
After casting the returned array to ASCII you can access all its public methods and properties. While this isn’t the way that attributes are normally used it is perfectly valid.

As well as using the constructor to initialise the instance, you can also use named parameters to initialise member variables. For example, if we add a public variable MoreData to the attribute class:

public class ASCII : Attribute
{
	public string MyData;
	public string MoreData;
	public ASCII(string MyDataIn)
	{ MyData = MyDataIn; }
}
…it can be initialised using:
[ASCII("Hello Attribute World",
	MoreData="SomeData")]
Now when GetCustomAttributes is called the instance is constructed as follows:
attrs[0]=new ASCII(
	"Hello Attribute World");
attrs[0].MoreData = "SomeData";
Once again you can test this assertion by using a MessageBox:
ASCII[] attrs=(ASCII[]) typeof(MyClass).
	GetCustomAttributes(
	typeof(ASCII), false);
MessageBox.Show(attrs[0].MoreData);
…where you will discover that the public string MoreData has indeed been set to “SomeData” by the attribute.

You should now be able to see the general principle. Any unnamed parameters used in the attribute are passed to the constructor in the order in which they are given. Any named parameters are used as initialisation statements after the instance has been constructed. Notice that all of the unnamed parameters have to come before the named parameters. So for example, an attribute applied as:

[MyAttribute(a,b,c,...
	name1=A,name2=B...)]
…results in:
MyAttribute MyInstance=new
	MyAttribute(a,b,c,...);
MyInstance.name1=A;
MyInstance.name2=B;
...
…when the GetCustomAttributes method is used. Notice that named parameters are optional, if they are not supplied then the corresponding variable is left uninitialised. It is also important to know that the type of parameter you can use is limited to the majority of the simple types, i.e. int, long, float, string, System.Type, object, a publicly accessible enum and a one-dimensional array of any allowable type. You might think that as you can pass an object you can pass anything but this isn’t the case. There is an additional condition that restricts parameters to constants of the appropriate type. So you can have:
[ASCII("Hello Method",MyObject=1)]
…and even:
[ASCII("Hello Method",MyObject="Hello")]
…but you can’t have:
[ASCII("Hello Method",
	MyObject=new object())]
…or anything like it.

Each time you request the details of an attribute a new object is instantiated and there are times when knowing this might be important. So for example, if we go back to the simpler version of ASCII with a single parameter constructor, the following:

[ASCII("Hello Attribute World")]
public class MyClass
{ }
[ASCII("Hello Again")]
public class MyClass2
{ }
…does indeed create two distinct attribute instances as you can demonstrate:
ASCII[] attrs1=(ASCII[])
	typeof(MyClass).GetCustomAttributes(
	typeof(ASCII), false);
ASCII[] attrs2=(ASCII[])
	typeof(MyClass2).GetCustomAttributes(
	typeof(ASCII), false);
MessageBox.Show(attrs1[0].MyData);
MessageBox.Show(attrs2[0].MyData);
The result is two messageboxes that correctly show each of the messages set by the attribute tags.

What isn’t generally realised is that you can change attribute values fairly easily at runtime. The reason is, of course, that the instances of the attribute classes that are created are perfectly normal objects and can be used without restriction. For example, we can get the object:

ASCII[] attrs1=(ASCII[])
	typeof(MyClass).GetCustomAttributes(
	typeof(ASCII), false);
…change the value of its public variable and show that it has changed:
attrs1[0].MyData="A New String";
MessageBox.Show(attrs1[0].MyData);
…and finally create another instance and show that its value is unchanged:
ASCII[] attrs3=(ASCII[])
	typeof(MyClass).GetCustomAttributes(
	typeof(ASCII), false);
MessageBox.Show(attrs3[0].MyData);

Practical attributes

Now that we have the basic mechanism that is used to implement attributes out in the open and perfectly clear we need to investigate some of the intricacies of using attributes in a real situation.

The first thing to say is that you can apply attributes to more than just a class. To restrict what an attribute can be applied to you need to apply the AttributeUsage attribute to the class that is associated with your custom attribute. For example:

[AttributeUsage(AttributeTargets.Method)]
public class ASCII : Attribute
…restricts the ASCII attribute to being applied to methods only. If you try to apply it to anything else the compiler will generate an error message. Intellisense provides a list of AttributeTargets and you can OR targets together to produce a set of things that the attribute can be applied to.

Being able to tag other entities with attributes raises the question of how to get at them at runtime? The answer is that there are a number of overloaded versions of GetCustomAttributes which return arrays of attributes associated with different types of entity. For example, GetCustomAttributes(memberinfo) will retrieve an array of attributes applied to the member specified by the memberinfo class. To see how this works first add a member function to MyClass and tag it with the ASCII attribute:

public class MyClass
{
	[ASCII("Hello Method")]
	public void MyMethod()
	{ }
}
To retrieve attributes associated with a member we first have to retrieve its memberinfo. There are many ways of doing this but in most cases specifying it by name is sufficient using the GetMember function:
System.Reflection.MemberInfo[] member =
	typeof(MyClass).GetMember("MyMethod");
The GetMember function retrieves all members that match the name specified string which can include wildcard characters. In this case it should just return a memberinfo class corresponding to the unique MyMethod function. To retrieve any attributes applied to the method we can use the usual:
Attribute[] attrs =
	Attribute.GetCustomAttributes(
	member[0]);
Or if you just want the ASCII attributes that have been applied we can use the alternative:
ASCII[] attrs =(ASCII[]) member[0].
	GetCustomAttributes(
	typeof(ASCII), false);
MessageBox.Show(attrs[0].MyData);
The GetMember function will return members of a range of types – methods, properties etc. You can be more specific if you want to and use functions such as GetMethod to return a single MethodInfo class that is specified by name and other information. For example:
System.Reflection.MethodInfo
	method = typeof(MyClass).
	GetMethod("MyMethod");
ASCII[] attrs =(ASCII[]) method.
	GetCustomAttributes(
	typeof(ASCII), false);
There is also a GetMethods function which will return an array of methods that match a name that includes wildcard characters. You can return fields, properties, interfaces and so on and use the corresponding GetCustomAttributes to return an array of attributes applied to them.

Retrieving attributes is generally straightforward – use reflection to get the entity and then call GetCustomAttributes. One slightly more complicated situation is when an attribute is applied to a parameter. In this case we have to first retrieve the method, then the parameters and then the attributes. To see this in action change the ASCII attribute to read:

[AttributeUsage(AttributeTargets.Method |
	AttributeTargets.Parameter)]
public class ASCII : Attribute
{
	public string MyData;
	public string MoreData;
	public ASCII(string MyDataIn)
	{
		MyData = MyDataIn;
	}
The attribute can now be applied to methods and parameters e.g.:
public class MyClass
{
	[ASCII("Hello Method")]
	public void MyMethod(
		[ASCII("Hello MyParameter")]
		string MyParameter)
	{
	}
}
To retrieve the attribute we first need to get the method:
System.Reflection.MethodInfo method =
	typeof(MyClass).GetMethod(
	"MyMethod");
Then we need to get the parameters of the method:
System.Reflection.ParameterInfo[]
	pars= method.GetParameters();
Finally we can get and access the parameter attribute:
ASCII[] attrs = (ASCII[]) pars[0].
	GetCustomAttributes(
	typeof(ASCII), false);
MessageBox.Show(attrs[0].MyData);
As well as controlling what an attribute can be applied to, you can also control how many times it can be applied using the AttributeUsage attribute and whether or not it is inherited. For example if you change the ASCII class definition to:
[AttributeUsage(AttributeTargets.Method |
	AttributeTargets.Parameter,
	AllowMultiple=true,Inherited=true )]
public class ASCII : Attribute
…you can place multiple attributes on a single entity and the attribute is inherited. For example, following:
[ASCII("Hello Method")]
[ASCII("Hello some more")]
public void MyMethod(string MyParameter)
{
}
GetCustomAttributes will return an array with two instances of ASCII with MyData set to the corresponding string. Similarly, if you create a new class that inherits from MyClass the attributes are also inherited. For example:
public class MyClass3 : MyClass
{
}
…has two ASCII attributes exactly the same as MyClass. You can, of course, forbid multiple attributes and stop attribute inheritance by setting the AllowMultiple and Inherited to false.

An example

It is very difficult to find a convincing practical example that uses attributes. If you look at the way attributes are used in C# and in the Framework you will soon discover that they have a limited application without extending the way that they behave to affect the compiler. Of course using custom attributes you can’t do this. Some built-in attributes do give you a good idea of what they are good at without extra tricks. For example, the whole P/invoke and Com interop marshalling of parameters is a perfect example. You apply marshalling attribute tags to the parameters of external methods you want to call and when you use them the Framework/CLR uses these attributes to decide how to transform managed data into the correct unmanaged data and vice versa. What makes this a good use of attributes is that the marshalling has to be applied at the class level and once applied isn’t going to be changed at the instance level, and the code that processes the instances can keep all of the mechanisms that load and respond to the attributes applied hidden from view. As far as the user of the attributes is concerned it’s a matter of applying the attribute, then ignoring it in all the code they write, but when they use some supplied machinery the attribute makes it work correctly.

Consider for a moment the problem of assigning a format to the fields of a struct using a new attribute. Implementing a rough sketch of such a facility reveals quite a lot about the difficulties inherent in doing a “good job” using attributes. The idea is simple, the implementation quickly becomes complicated. First we need a new attribute:

[AttributeUsage(AttributeTargets.Field,
	AllowMultiple = false,
	Inherited = false)]
public class Formattable : Attribute
{
	public string format;
	public Formatable(string _format)
	{
		format = _format;
	}
}
There is nothing new here but it is worth saying that it would be better to implement the format string as a get/set property. This attribute can now be applied to any field in any struct or class. Notice that you can restrict its application to say a field in a struct rather than a class.

Now that we have an attribute we can apply it:

public struct MyData
{
	public int notacost;
	[Formatable("Money")]
	public int cost;
}
Here we have a simple struct with two int fields one of which, cost, will hold a currency value and hence is tagged with the Formattable attribute set to Money. Immediately we have a problem in that the user can enter any string they care to – not just the few we want to allow. One way of restricting the input to an attribute is to define an enum. For example:
public enum FormatType
{
	Money, Percentage,
}
With this enum the attribute definition changes to:
[AttributeUsage(AttributeTargets.Field,
	AllowMultiple = false,
	Inherited = false)]
public class Formattable : Attribute
{
	public FormatType format;
	public Formatable(FormatType _format)
	{
		format = _format;
	}
}
…and the struct becomes:
public struct MyData
{
	public int notacost;
	[Formatable(FormatType.Money)]
	public int cost;
}
Now the user really can only enter values that you provide.

The next task is to create some machinery to process the attribute. In this case we can assume that some sort of method has to be provided that displays the struct, taking notice of the attributes. The simplest way of providing this machinery is to introduce a static class – after all why should we need to create instances of this machinery?

public static class Formatter
{
The first problem is what to pass to the method that does the formatting? In principle we could pass any struct or class to the method and there really isn’t a type-safe way of doing this. However as long as we actually test the type passed to the method this should at least be runtime type-safe. If you want to restrict what can be passed to structs you can, using:
static public void display(ValueType a)
Unfortunately this also allows simple types such as int and long to be passed so you need to add another test. The problem is that struct isn’t a type, it’s a keyword that creates a struct which inherits directly from ValueType as do all simple types. This means you can’t separate all structs from the rest of the ValueTypes. You can’t even simply test to see if what has been passed in has fields to determine if it is a struct because simple types like int also have fields!

Moving on to provide the mechanism for inspecting and processing the attributes, the first step is to get all the fields:

System.Reflection.FieldInfo[]
	fields= a.GetType().GetFields();
Next we step through the array and process each field in turn:
foreach (System.Reflection.FieldInfo
	field in fields)
{
We need the array of Formattable attributes that have been applied and in this case we also need the value of the field:
Formatable[] formats=
	(Formatable[])field.
		GetCustomAttributes(
		typeof(Formatable), false);
int temp = (int)field.GetValue(a);
Notice that we should also check that the type of the field is suitable for the formatting about to be applied but this has been omitted for simplicity.

Now we can use the Formattable attribute object to determine what format string to store in format:

string format="";
if (formats.Length!=0)
{
	if (formats[0].format == FormatType.Money)
	{
		format="C";
	}
}
Notice there can only be one Formattable object because of the:
AllowMultiple = false
Finally we can display the result and close the foreach loop, method and class:
		}
	MessageBox.Show(temp.ToString(
		format));
	}
}
Now you can write:
MyData SomeData = new MyData();
SomeData.cost = 123;
SomeData.notacost = 456;
Formatter.display(SomeData);
The cost field, tagged as money format, will display with a currency symbol and the notacost field will display without. Unfortunately as it stands the display method also accepts simple types so:
Formatter.display(1);
…displays the two fields associated with a boxed int. There seems to be no elegant way of stopping this from happening. One solution would be to require the Formattable attribute to be applied to the entire struct before any of its fields are processed. That is, change the attribute to:
[AttributeUsage(AttributeTargets.Field|
	AttributeTargets.Struct,
	AllowMultiple = false,
	Inherited = false)]
public class Formattable : Attribute
…add a constructor that takes no parameters and add a test that the passed-in parameter did indeed have a Formattable attribute before processing it.

Finally, a new feature introduced in C# 3.0 might make attributes more useful. The static method that implements the machinery would be better associated with the struct or, in general, the types to which it applies. The new extension method facility can be used to retrofit methods to classes that already exist. You can even add new methods to built-in types such as int or string.

Unfortunately the problem that structs inherit directly from ValueType makes it impossible to add an extension method to all structs and nothing else. You can easily add an extension method to a single named struct but why bother… you might as well just add the method to the struct directly. To add the display method to all value types you simple change its definition to:

static public void display(
	this System.ValueType a)
That is, add the modifier “this” to the first parameter. Now you can call the display method using:
SomeData.display();
In other words, the display method has been added to every struct you create – powerful, isn’t it! Unfortunately it has actually been added to every value type so you can also write:
int i = 10;
i.display();
…which is perhaps not what you intended. In this case it will display the two fields supported by a boxed int i.e. the maximum and minimum values.

Conclusion

Attributes are something that you probably won’t use every day, but now that you know exactly how they work you can spot when the approach might be useful.


Dr. Mike James’ programming career has spanned many languages, starting with Fortran. The author of Foundations of Programming, he has always been interested in the latest developments and the synergy between different languages.

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“The most exciting phrase to hear in science, the one that heralds new discoveries, is not 'Eureka!' but 'That's funny...'” - Isaac Asimov