Wednesday, January 24, 2007

When are attributes created?

Speaking of Attributes, there's often a lot of misconceptions about how they are handled by the compiler and at runtime. Some of this is because of not reading the documentation properly, but also a lot of it is compounded by the fact that there are actually different kinds of attributes that look the same but are handled differently by the compiler and the CLR.

Take, for instance, ObsoleteAttribute: this attribute, innocently enough, behaves like any other custom attribute. It is written into the compiled metadata for your assembly. It can be reflected at runtime. But, as you know, it also triggers a specific response in the compiler; it generates a warning or error informing the programmer that the marked element is deprecated.

This can generate a false conception for the developer that attributes are instantiated a compile-time by the compiler. This is not true; ObsoleteAttribute just happens to be a special case handled specifically by the compiler.

You should also be aware of a type of attribute known as a "pseudo-attribute." These attributes are not written to the compiled metadata for your assembly, and thus cannot be reflected at runtime. Again, they are typically interpreted by the compiler in some way.

For example, there is the pseudo-attribute AssemblyVersionAttribute. This attribute tells the compiler what version to assign to the final assembly produced. If you attempt to reflect this attribute at runtime, you will find that it doesn't actually exist on your assembly (you have to use Assembly.GetName().Version).

The ultimate thing you have to keep in mind is that attributes are not instantiated until someone requests them. The compiler does this on its own in certain cases, but for the most part it just does a compile-time verification of the code and then passes the attribute on as meta-data into the IL.

For an example, assume that we have defined a custom attribute called TimeStampAttribute that takes a single string in its constructor that it wants to parse into a DateTime value. We want to use this to mark when we created certain methods:

C#:

[TimeStamp("fred")]
public void MyMethod()
{
// ...
}


VB.NET:

<TimeStamp("fred")> _
Public Sub MyMethod()
' ...
End Sub


But wait! That code won't work, right? It's supposed to have a date/time that we can parse like "1/24/2007 11:46 PM" instead of "fred." We're going to get a FormatException as soon as we run, right? Wrong.

The code of course compiles just fine, since "fred" is a string like we asked for. The code will even run just fine for the most part since the attributes aren't created explicitly. You won't get the expected exception raised until you make a call to GetCustomAttributes() that ends up iterating over the attributes in question.

No comments: