Adding Custom Metadata to .NET Elements


The Reflection APIs work by querying the metadata stored in a .NET assembly. Custom attributes are a simple way to extend the metadata of any given managed element. Using custom attributes, you can add extra information to an assembly's metadata and then query for this extra information at runtime.

Defining a Custom Attribute

A custom attribute is a declarative programming construct that allows you to extend a language element's metadata. This information is stored in an assembly's metadata and can be retrieved at runtime. A corresponding attribute class must exist before an attribute can be used to decorate a language element. All attribute classes inherit from System.Attribute . The attribute class contains properties that store and retrieve the extra declared metadata. Listing 13.26 demonstrates how to define a custom attribute.

Listing 13.26
 C# [AttributeUsage(AttributeTargets.ClassAttributeTargets.Struct)] class BusinessObjectAttribute : System.Attribute {   private string m_DBName;   private string m_TableName;   private string m_QueryString;   public BusinessObjectAttribute(string dbName,                                  string tableName) {     m_DBName = dbName;     m_TableName = tableName;   }   public string Database{     get{ return m_DBName; }     set{ m_DBName = value; }   }   public string Table {     get{ return m_TableName; }     set{ m_TableName = value; }   }   public string QueryString {     get{ return m_QueryString; }     set{ m_QueryString = value; }   } } VB <AttributeUsage(AttributeTargets.Class Or AttributeTargets.Struct)> _ Class BusinessObjectAttribute     Inherits System.Attribute     Private m_DBName As String     Private m_TableName As String     Private m_QueryString As String     Public Sub New(ByVal dbName As String, _                    ByVal tableName As String)         m_DBName = dbName         m_TableName = tableName     End Sub     Public Property Database() As String         Get             Return m_DBName         End Get         Set(ByVal Value As String)             m_DBName = Value         End Set     End Property     Public Property Table() As String         Get             Return m_TableName         End Get         Set(ByVal Value As String)             m_TableName = Value         End Set     End Property     Public Property QueryString() As String         Get             Return m_QueryString         End Get         Set(ByVal Value As String)             m_QueryString = Value         End Set     End Property End Class 

The code declares a new Attribute named BusinessObjectAttribute . This attribute is intended to be applied to classes or structs. Astute readers will notice that the attribute class itself is in turn decorated with an attribute, AttributeUsage . The AttributeUsage attribute describes how a custom attribute can be used. The AttributeUsage has three properties: the required AttributeTarget property, the optional AllowMultiple property, and the optional Inherited property.

The AttributeTargets property specifies the language elements on which the attribute can be applied. The values of the AttributeTargets enumeration can be combined to specify multiple targets. In the previous example, the BusinessObjectAttribute can be applied to classes and structs. Table 13.12 shows all the possible AttributeTargets values.

Table 13.12. AttributeTargets Enumeration Value

MEMBER

All

Assembly

Class Module

Constructor

Delegate Struct

Enum

Field

Interface

Method

Module

Parameter

Event

Property

ReturnValue

Struct

CLARIFY WHAT LANGUAGE ELEMENT AN Attribute APPLIES TO

Usually, the attribute will directly precede the language element to which it applies. However, position of the attribute is not always enough to determine to which element the attribute applies. For instance, consider this snippet:

 
 C# [Attribute()] public int Function(int) {...} 

In this instance, there is no way to tell whether the attribute is intended for the method element or for the method element's return value. To clarify which element the attribute applies to, you prefix the attribute name with the AttributeTargets enumeration value that describes which language element to which it applies.

 
 C# [returnvalue:Attribute] public int Function(int) {...} 

The AllowMultiple property specifies whether the attribute can be used more than once on the same language element. This value is option and false by default. In the preceding example, the BusinessObjectAttribute can appear only once on the same class or struct.

The inherited property specifies whether the attribute is inherited by derived classes. This value is optional and is false by default. In the example the BusinessObjectAttribute will not be inherited by classes that derive from a class that is decorated with this attribute.

All custom attributes must inherit from the System.Attribute class. Also, attributes should use the Attribute suffix in their names . When the attribute is used, the Attribute suffix does not need to be included. Instead, the class name minus the Attribute suffix becomes an alias for the class. Listing 13.27 demonstrates how the BusinessObjectAttribute class can be applied to a class.

Listing 13.27
 C# [BusinessObject("CustomerRecordsDB", "Customers")] class Customer { } VB <BusinessObject("CustomerRecordsDB", "Customers")> _ Public Class Customer End Class 

Here the BusinessObject name is actually an alias for the BusinessObjectAttribute class. It is no mistake that the snippet's attribute declaration appears similar to a construction call. The two parameters correspond to the two parameters to the attribute class's constructor. The parameters must appear in the same order as the parameters in the constructor declaration. It is also possible to specify properties that do not appear in the constructor's parameter list. Listing 13.28 demonstrates how to initialize the QueryString property of the BusinessObjectAttribute when it is applied to a class.

Listing 13.28
 C# [BusinessObject("CustomerRecordsDB",                 "Customers",                 QueryString="SELECT * FROM CUSTOMERS")] class Customers { } VB <BusinessObject("CustomerRecordsDB", _     "Customers", _     QueryString="SELECT * FROM CUSTOMERS")> _ Public Class Customer End Class 

This code demonstrates how to use a named parameter to initialize a property that does not appear in the attributes constructor. The name of the named parameter corresponds to the property's accessor name, not the field name.

Retrieving Custom Attributes

Now that you have defined a custom attribute and applied the attribute to a language element, it is time to retrieve that attribute at runtime. You can retrieve custom attributes by using the GetCustomAttributes methods of the System.MemberInfo class. This method comes in these two flavors:

 
 object [] GetCustomAttributes(bool) object [] GetCustomAttributes(Type, bool) 

To retrieve all of the custom attributes of a class, you can use the GetCustomAttributes method that takes one bool parameter. This bool parameter specifies whether to search the member's inheritance chain to find the attributes. This method returns either an array of all the custom attributes or an array of zero elements if no attributes are defined. Listing 13.29 demonstrates how to use the GetCustomAttributes method.

Listing 13.29
 C# [BusinessObject("CustomerRecordsDB", "Customers")] class Customer { } class CustomAttributeTest {   public static void Main() {     Type customerType = typeof(Customer);     Object[] atts = customerType.GetCustomAttributes(false);     foreach(Attribute att in atts) {       if(att is BusinessObjectAttribute) {         BusinessObjectAttribute b = (BusinessObjectAttribute)att;           MessageBox.Show("Database: " + b.Database +                             "\nTable: " + b.Table);         }     }   } } VB Module Module1     <BusinessObject("CustomerRecordsDB", "Customers")> _     Public Class Customer     End Class     Sub Main()         Dim cust As New Customer()         Dim customerType = cust.GetType()         Dim atts() = customerType.GetCustomAttributes(False)         Dim i As Int32         For i = 0 To atts.Length - 1             If TypeOf atts(i) Is BusinessObjectAttribute Then                 Dim b = CType(atts(i), BusinessObjectAttribute)                 MessageBox.Show("Database: " & b.Database & _                        Chr(13) & "Table: " + b.Table)             End If         Next i     End Sub End Module 

To retrieve all of the custom attributes of a class that can be assigned to a given type, use the GetCustomAttributes method that takes two parameters. The first parameter is the type of the attribute for which to search. Only attributes that are assignable to this type will be returned. The second parameter specifies whether to search the member's inheritance chain to find the attribute. This parameter is identical to the sole parameter of the other GetCustomAttributes method overload. This method returns either an array of all the custom attributes or, if no attributes that are assignable to the specified type are defined, an array of zero elements. Listing 13.30 demonstrates how to use GetCustomAttributes to retrieve only BusinessObjectAttribute attributes.

Listing 13.30
 C# [BusinessObject("CustomerRecordsDB", "Customers")] class Customer { } class CustomAttributeTest {   public static void Main() {     Type customerType = typeof(Customer);     Object[] atts = customerType.GetCustomAttributes(         typeof(BusinessObjectAttribute), false);     foreach(BusinessObjectAttribute att in atts) {       MessageBox.Show("Database: " + att.Database +                         "\nTable: " + att.Table);     }   } } VB Module Module1   <BusinessObject("CustomerRecordsDB", "Customers")> _   Public Class Customer   End Class   Sub Main()     Dim boa = New BusinessObjectAttribute(String.Empty, String.Empty)     Dim cust As New Customer()     Dim customerType = cust.GetType()     Dim atts() = customerType.GetCustomAttributes(boa.GetType(), False)     Dim i As Int32     For i = 0 To atts.Length - 1       If TypeOf atts(i) Is BusinessObjectAttribute Then         Dim b = CType(atts(i), BusinessObjectAttribute)         MessageBox.Show("Database: " & b.Database & _                         Chr(13) & "Table: " + b.Table)       End If     Next i   End Sub End Module 


Microsoft.NET Compact Framework Kick Start
Microsoft .NET Compact Framework Kick Start
ISBN: 0672325705
EAN: 2147483647
Year: 2003
Pages: 206

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net