With the inclusion of Reflection and Attributes within the .NET framework, Microsoft has taken software development to a new level. In this article, we will look at how the combination of these two technologies can result in some generic and powerful software.
After an introduction to .NET Attributes, we look at two examples of attributes in action that, with some enhancement, can be utilized in your .NET programming project today: a generic database manager for managing objects, and a custom Bug Tracking attribute along with a sample viewer application.
Before we can explore the construction of both the generic database and bug tracking attributes, we need an understanding of what attributes are and how they are used.
For supplemental information to this overview, please see the MSDN help packaged in .NET.
To work with the code in this article, you will need the following tools:
- .NET Framework Beta 2
- SQL Server 7.0 or 2000
- Visual Studio.NET Beta 2 (or a text editor)
.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 .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
http://www.devarticles.com/c/a/ASP.NET/Demonstrating-Attributes-and-Reflection-in-.NET/1/