Demonstrating Attributes and Reflection in .NET - What are Attributes? (Page 2 of 8 )
.NET Attributes provide the means for a developer to add meta-data that describes, or annotates, specific elements of code such as classes, methods, properties, etc. At compile time, the resulting metadata is placed into the Portable Executable (PE)file, along with the Microsoft Intermediate Language (MSIL). Once metadata is in the PE, other .NET programs may access it using the .NET Reflection API.
Syntactically, attributes are applied using the [AttributeName([Parameters])] notation, where AttributeName is the name of the attribute, and Parameters is a listing of parameters required by the Attribute's constructor. The following is an example of the .NET Serializable attribute applied to a class. The Serializable attribute indicates that a class can be serialized.
[Serializable] Public class MyClass { public MyClass() { // Nothing to see here. } . . . }
.NET provides a number of attributes with .NET Beta 2. If you have tried your hand at writing Web Services with .NET, you undoubtedly had to apply the [WebMethod] attribute to any methods exposed to your Web Service users.
Keeping in line with .NET's extensibility, developers may write custom attributes by sub-classing the System.Attribute class.
Custom Attributes
Creating custom attributes is a fairly simple process. The steps are summarized below:
Create the custom attribute by deriving a class from System.Attribute
If required, define the properties associated with an attribute. Some attributes such as the Serializable attribute shown above do not have properties, and merely indicate a fact about some code.
Define a Constructor for the attributes that contains the necessary arguments (if any) to construct the attribute.
Optionally, specify the scope of the custom attribute by specifying the AttributeUsage attribute (Yes, attributes can have their own attributes!)
The following is an example of a simple attribute that developers may use to indicate the stability of their code:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public class CodeUsabilityMeter : Attribute { // scale of 1- 10 private int _usabilityRating; // the name of the developer private string _developer; /// /// indicates how usable a section of code is /// on a scale of 1 - 10. /// public int UsabilityRating { get { return _usabilityRating; } set { _usabilityRating = value; } } /// /// the name of the developer /// public string Developer { get { return _developer; }
set { _developer = value; } } /// /// overloaded. constructs a CodeUsabilityMeter /// Attribute /// /// on a scale /// of 1 - 10 how usable the code is /// /// /// the name of the developer /// public CodeUsabilityMeter(int iUsability, string sDev) { this.UsabilityRating = iUsability; this.Developer = sDev; } }
Note the AttributeUsage attribute specified at the beginning of the attribute definition. The AttributeUsage attribute defines how an attribute can be used within an assembly. The AttributeUsage attribute contains the following properties that control a custom attribute's usage:
ValidOn(type: AttributeTargets): Defines the places within the code where an attribute may be placed. Possible values are: Assemblies, Classes, Constructors, Delegates, Enumerations, Events, Fields, Interfaces, Methods, Modules, Parameters, Properties, Return Values and Structures. The default value for this parameter is All.
AllowMultiple(type: bool): If true, the attribute may be applied more than once to a specific code element. The default is false.
Inherited(type: bool): If true, the attribute is inherited by derived classes. The default is false.
Getting back to the CodeUsabilityMeter attribute: we derived from the System.Attribute class, defined the properties specific to the attribute, and provided a constructor to initialize the attribute's properties.
Now that we have defined our custom attribute, lets put it to use. Here is an example of the CodeUsabilityMeter applied to methods within a class:
public class MyClass { public MyClass() {
} // indicate that this code is as // good as it gets [CodeUsabilityMeter(10, "John Smith")] public int SolidCode() { return 2 + 2;
} // this attribute (with a ranking of 1) indicates that the probability of //this code will // defintely crash [CodeUsabilityMeter(1, "Dave Q. Badprogrammer")] public int ShakeyCode() { int i = 0; return 2/i; } }
Using .NET reflection and our newly created custom attribute, we could write code that evaluates all .NET assemblies within a project, and produces a report listing areas of code that aren't quite ready.
Now that we have a good foundation on .NET attributes, we will dig into some code that defines some custom attributes, and uses .NET reflection to interrogate attributes and do something with it. For a complete code listing of the examples used in this article, please follow the Download Support Material link at the end of this article.