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.
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.
C# [AttributeUsage(AttributeTargets.Class|AttributeTargets.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.
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 TOUsually, 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.
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.
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.
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.
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 + " Table: " + 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.
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 + " Table: " + 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 |
3.145.109.234