FREE BOOK

Chapter I: Attribute Fundamentals

Posted by Apress Free Book | C# Language December 09, 2008
IN THE COURSE OF DEVELOPING an application, it is quite typical to have core functionality contained in methods that are invoked by other specialized methods.

Compiling Attributes

Fortunately, from a compilation perspective, there is nothing that you need to do differently when you include attributes in your C# code. You don't need to check a check box in Visual Studio .NET or add a switch to csc.exe to have your attributes show up in the resulting assembly. For example, if you look at an assembly that contains attributes in ILDasm, you'll see something that looks like Figure 1-5.



Figure 1-5. The .custom directives in an assembly

10. This has nothing to do with whether or not the attribute class is sealed.

The custom attribute will be stored within a .custom directive. The location of the directive will vary depending on the target stated in the C# code. In this case, ObsoleteAttribute was used on the BadCountry class. The directive will state the full name of the attribute type, along with the constructor used in the attribute declaration.

Unfortunately, space limitations prevented us from showing the full story in Figure 1.5. There is something after the equal sign that couldn't fit in the image. Listing 1-7 shows what ends up on the other side.

Listing 1-7. The Full Attribute Format in an Assembly

.class public auto ansi beforefieldinit BadCountry
extends [mscorlib]System.Object
{
.custom instance void
[mscorlib]System.ObsoleteAttribute::.ctor(string,
bool) =( 01 00 40 54 68 69 73 20 63 6C 61 73 73 20 73 68 // ..@This class sh
6F 75 6C 64 20 6E 6F 20 6C 6F 6E 67 65 72 20 62 // ould no longer b
65 20 75 73 65 64 20 2D 20 73 77 69 74 63 68 20 // e used - switch
74 6F 20 49 6D 70 72 6F 76 65 64 43 6F 75 6E 74 // to ImprovedCount
72 79 2E 01 00 00 ) // ry....
} // end of class BadCountry

You can glean some general ideas as to what the bytes represent, but you do not need to know the full details of the format to use and create attributes successfully. As you'll see in Chapter 4, the API to read metadata from an assembly is rather straightforward and does not require you to know the layout of the byte array.

NOTE For those who are curious to know what the bytes stand for, Appendix of this book goes through the format in excruciating detail. Most attributes will end up in a .custom directive. Interestingly enough, they are known as custom attributes. However, there is a special set of attributes that, when present in code, will end up in other places in the assembly. These are known as pseudo-custom attributes.11 You declare them in the same way that you declare any other attribute in your C# code, but the end results in the assembly are vastly different. For example, take a look at the following code:

[assembly: AssemblyVersion("1.0.0.0")]

When you compile your code, you won't find a .custom directive with a type name that contains AssemblyVersionAttribute. Figure 1-6 shows what happens when you use AssemblyVersionAttribute.



Figure 1-6. Pseudo-custom attributes in assemblies

In this case, the .ver directive contains the version information of the assembly.

There is a reason why pseudo-custom attributes are stored in this fashion: it's more efficient. Pseudo-custom attributes don't have the overhead of storing a bunch of bytes that a normal custom attribute requires. An explicit example of this is when you make a class serializable, like this:

[type: Serializable]
public
class SerializableCountry
{
   
// ...
}

The ILDasm results are as follows:

.class public auto ansi serializable beforefieldinit SerializableCountry
extends [mscorlib]System.Object
{
} // end of class SerializableCountry

Notice that the serializable attribute now ends up in the class definition. There is no .custom directive anywhere on the class. What is really going on is that a bit flag is being set for SerializableCountry. When you use the SerializableAttribute attribute with the class, here's what the token looks like:

TypDefName: Apress.NetAttributes.SerializableCountry (02000008)
Flags : [Public] [AutoLayout] [Class] [Serializable] [AnsiClass] (00102001)

Without the SerializableAttribute attribute, you'll notice that a value in the
Flags field changes:

TypDefName: Apress.NetAttributes.SerializableCountry (02000008)
Flags : [Public] [AutoLayout] [Class] [AnsiClass] (00100001)

Pseudo-custom attributes have this special storage consideration because they are used extensively by compilers or by the CLR. It's much faster for the CLR to look at a bit field to see if it's serializable, rather than to go through all of the custom attributes (if any exist) to see if one of them is of the SerializableAttribute type. Of course, with this efficiency comes a trade-off in verbosity. A class is either serializable or it's not serializable. Contrast that with the ObsoleteAttribute, which lets you give a client a helpful message and make obsolete elements cause either a warning or an error during compilation.

SOURCE CODE The code for these examples is in Chapter1\Country and Chapter1\CountryClient.

Conclusion

In this chapter, you learned the following about attributes:

  • Where data can be stored in and around code, and how that compares to attributes
     
  • How to declare attributes within C# code
     
  • Where attributes end up in an assembly In the next chapter, you'll get the details on how some attributes are used directly during the compilation process and the effects of using these attributes.

Total Pages : 7 34567

comments