Creating and Using Custom Attributes with VB.NET

Environment: .NET Framework V1.0, Win 2K, XP

Introduction

The common language runtime allows programmers to add descriptive declarations to programming elements called Attributes. Attributes are like annotations that can be applied to assemblies, types, methods, or properties. Attributes are emitted into the assembly metadata and they can be extracted by using Reflection services of the .NET framework.

Attributes are very convenient to the programmers because, with little effort, the runtime behavior of the program elements can be modified. For example, the attribute Serializable added to any class will indicate that the class can be serialized. As another example, consider the Transaction attribute of ServicedComponent class. Just adding this attribute to the class determines its transactional behavior.

Creating Custom Attributes

Custom attributes are nothing but normal classes that directly or indirectly derive from System.Attribute. Listed below are the steps required to create the custom attributes:

1. Applying the AttributeUsage attribute

The declaration of a custom attribute itself begins with the usage of an attribute, the AttributeUsage attribute. This has three members that qualify a custom attribute:

  1. AttributeTargets member
  2. This member specifies to what program elements the custom attribute can be applied. For example, AttibuteTargets.Class indicates that the attribute can be applied to a class, and AttributeTargets.Method indicates that the attribute can be applied to a method.

    Given below is a list of possible attribute targets:

















    Attribute Target Description
    All Attribute can be applied to any application element.
    Assembly Attribute can be applied to an assembly.
    Class Attribute can be applied to a class.
    Constructor Attribute can be applied to a constructor.
    Delegate Attribute can be applied to a delegate.
    Enum Attribute can be applied to an enumeration.
    Event Attribute can be applied to an event.
    Field Attribute can be applied to a field.
    Interface Attribute can be applied to an interface.
    Method Attribute can be applied to a method.
    Module Attribute can be applied to a module.


    Note:    Module refers to a portable executable file (.dll or .exe) and not a Visual Basic standard module.

    Parameter Attribute can be applied to a parameter.
    Property Attribute can be applied to a property.
    ReturnValue Attribute can be applied to a return value.
    Struct Attribute can be applied to a structure; that is, a value type.

    AttributeTargets enumeration values can also be combined with a bitwise OR operation to get the preferred combination. Given below is an example where the custom attribute can be applied either to a class or a method.

    <AttributeUsage (AttributeTargets.Class
    Or AttributeTargets.Method)>
    
  3. Inherited property

    The Inherited property indicates whether the custom attribute can be inherited by classes that are derived from the classes to which your attribute is applied. This property takes either a true (the default) or false flag. For example, in the following code example, MyAttribute has a default Inherited value of true.

    <AttributeUsage( AttributeTargets.All, Inherited := True)> _
    Public Class MyAttribute
           Inherits Attribute
    
    End Class
    
  4. AllowMutliple property

    The AllowMultiple property indicates whether multiple instances of the custom attribute can exist on an element. The value can be either true or false.

2. Initializing Attributes

We can use overloaded constructors to initialize attribute values. If properties are defined then, a combination of named and positional parameters can be used when initializing the attribute. Typically, all mandatory parameters are positional and all optional parameters are named. For example, consider an Attribute definition as shown below:

<AttributeUsage(AttributeTargets.Class Or _
AttributeTargets.Method)> Class DeveloperInfoAttribute
                          Inherits Attribute

    Private _Description As String
    Private _name As String
    Private _lastChanged As String

    Public ReadOnly Property Notes() As String
        Get
            Return _Description
        End Get
    End Property

    Public ReadOnly Property Author() As String
        Get
            Return _name
        End Get
    End Property

    Public Property LastChanged() As String
        Get
            Return _lastChanged
        End Get
        Set(ByVal Value As String)
            _lastChanged = Value
        End Set
    End Property

    Sub New(ByVal author As String, ByVal notes As String)
        _name = author
        _Description = notes
    End Sub

End Class

Here, note that Author and Notes are required parameters for the attribute whereas LastChanged is an optional parameter. So, LastChanged value can be initialized by using a named parameter as shown below:

<DeveloperInfoAttribute("Manoj", "This is a simple class to test
custom attributes", LastChanged:="5-May-2003")>

<DeveloperInfoAttribute("Somebody", "This is a dummy method.
Not to be used")>

A couple of things to note:


  • Though not mandatory, it is a good practice to add the word Attribute to the end of the attribute name.

  • Attributes can be used in an abbreviated fashion. Developers can choose to skip the word attribute from the name of the attribute. For example, we can have:
  • <DeveloperInfo("Somebody", "This is a dummy method.
    Not to be used")>
    

Retrieving Attribute Information

As mentioned earlier, attributes are emitted into the assembly and hence we can use Reflection to obtain the attributes defined for a particular programming element. More specifically, we use the GetCustomAttribute shared method of the Attribute class to obtain an instance of the custom attribute.

The code sample shown below obtains a reference to the custom attribute DeveloperInfoAttribute defined for the class TestAttr.


Dim CustAttr As DeveloperInfoAttribute
CustAttr = CType(Attribute.GetCustomAttribute(GetType(TestAttr), _
GetType(DeveloperInfoAttribute)),DeveloperInfoAttribute)

Likewise, the code sample shown below gets an instance of the custom attribute defined for the method Dummy of the TestAttr class

CustAttr = CType(Attribute.GetCustomAttribute(GetType(TestAttr). _
           GetMember("Dummy")(0), _
           GetType(DeveloperInfoAttribute)), _
           DeveloperInfoAttribute)

Putting It All Together

Attached with this article is a simple console application that demonstrates all the above concepts. Here, DeveloperInfoAttribute is a custom attribute that has three properties: Author, Notes, and LastChanged. Author and Notes are mandatory, whereas LastChanged is optional. This attribute can be applied to both classes and methods. TestAttr is a user-defined class on which attributes are defined.

Downloads

Download source – 10 Kb

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read