Among the most confusing and misunderstood elements of the .NET framework are the purpose and uses of attributes. Since attributes are new to both C++ and VB developers, there’s no context for easy comparisons to familiar language elements. But the addition of attributes to the Common Language Runtime (CLR) gives developers new abilities to associate information with their classes via an annotation mechanism, which the CLR can then use to operate on the objects at runtime.
Attributes can be used to document classes at design time, specify runtime information (such as the name of an XML field to be used when serializing information from the class), and even dictate runtime behavior (such as whether the class should automatically participate in a transaction). More importantly, you can develop your own attributes that specify architectural standards and enforce their use. In this article, we’ll look at how the CLR uses standard attributes and how and why you should create your own attributes.
What is an attribute?
Many .NET developers get their first exposure to attributes when using templates provided in the Visual Studio environment. For example, when a VB developer creates a new XML Web Service, they get back sample code that defines the Web Service to the CLR using attributes like this:
<WebService(Namespace := "http://tempuri.org/")> _
Public Class Service1
The class Service1 is said to have been “decorated” with the WebService attribute, and the NameSpace variable has been assigned the value of http://tempuri.org/. The WebService and WebMethod attributes signal the compiler that these attributes should be accessible using the SOAP protocol. As you can see from this example, the purpose of .NET attributes is to signal a compiler or the runtime to generate MSIL or to operate on the MSIL generated, based on metadata representing the attribute. There are many other examples of using attributes to instruct the compiler how to generate the appropriate MSIL, including:
- Using MarshalAsAttribute to tell the compiler how to marshal method parameters when interoperating with native code.
- Using COMClassAttribute to mark components as COM so the Visual Basic compiler will generate code allowing a .NET component to be called from COM.
- Using attributes to describe the resulting assembly with title, version, or description information. The version information is especially important when using signed assemblies and the Global Assembly Cache because you can force the runtime to load only particular versions of assemblies and avoid the COM DLL Hell problem.
- Mapping class members to specific XML node types when defining XML serialization.
When compiled, attributes are saved with the metadata of the finished assembly. At runtime, the CLR or your own programs can still use any attributes used by the compiler to control code generation by using reflection to query the assembly for the values specified by an attribute. The feature that makes attributes most powerful, however, is their ability to add additional capabilities to any language hosted within the .NET runtime without making changes to the language compilers.
Adding language capabilities
Attributes are not language specific. Just as we can decorate the HelloWorld method in VB with the WebMethod attribute, there’s a similar implementation for C#:
public string HelloWorld()
return "Hello World";
Since attributes are language independent, you can define new functionality by creating attributes that inherit from the System.Attribute class. You can then apply that functionality to programs written in any language by simply decorating the appropriate classes, methods, or properties at design time.
For example, one company that I’m working with has defined its own extensions to the CLR that implement metering via performance counters and dynamically created usage statistics. The company has implemented the functionality by creating a set of attributes that can be applied at the module, class, method, event, or property level at design time. Once these attributes are applied, the compiler can include their code to implement this functionality at compile time and the CLR can use reflection to collect the information defined by the attributes at runtime. Using this mechanism allows the company to implement standard metering and usage statistics functionality across all its .NET systems.
Attributes are a good thing
The implementation I’ve discussed here is just one example of how companies are using .NET attributes to standardize operations across their development efforts. .NET Architects who take the time to examine and apply this new technology will undoubtedly find other ways to use it in the applications they design.