Writing Custom Attributes

To design your own custom attributes, you do not need to master many new concepts. If you are familiar with object-oriented programming and know how to design classes, you already have most of the knowledge needed. Custom attributes are essentially traditional classes that derive directly or indirectly from System.Attribute. Just like traditional classes, custom attributes contain methods that store and retrieve data.

The primary steps to properly design custom attribute classes are as follows:

  • Applying the AttributeUsageAttribute
  • Declaring the attribute class
  • Declaring constructors
  • Declaring properties

This section describes each of these steps and concludes with a custom attribute example.

Applying the AttributeUsageAttribute

A custom attribute declaration begins with the AttributeUsageAttribute, which defines some of the key characteristics of your attribute class. For example, you can specify whether your attribute can be inherited by other classes or specify which elements the attribute can be applied to. The following code fragment demonstrates how to use the AttributeUsageAttribute.

[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
[Visual Basic]<AttributeUsage(AttributeTargets.All, Inherited := False, AllowMultiple := true)>

The System.AttributeUsageAttribute Class has three members that are important for the creation of custom attributes: AttributeTargets, Inherited, and AllowMultiple.

AttributeTargets Member

In the previous example, AttributeTargets.All is specified, indicating that this attribute can be applied to all program elements. Alternatively, you can specify AttributeTargets.Class, indicating that your attribute can be applied only to a class, or AttributeTargets.Method, indicating that your attribute can be applied only to a method. All program elements can be marked for description by a custom attribute in this manner.

You can also pass multiple instances of AttributeTargets. The following code fragment specifies that a custom attribute can be applied to any class or method.

[AttributeUsage (AttributeTargets.Class | AttributeTargets.Method)]
[Visual Basic]<AttributeUsage (AttributeTargets.Class Or AttributeTargets.Method)>

Inherited Property

The Inherited property indicates whether your 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, while YourAttribute has an Inherited value of false.

//This defaults to Inherited = true.
public class MyAttribute :Attribute
{
}

[AttributeUsage( Inherited = false)]
public class YourAttribute : Attribute
{
}
[Visual Basic]<AttributeUsage( AttributeTargets.All, Inherited := True)> Public Class _
MyAttribute
    Inherits Attribute
End Class

<AttributeUsage( AttributeTargets.All, Inherited := False)> Public Class _
YourAttribute
    Inherits Attribute
End Class

The two attributes are then applied to a method in the base class MyClass.

public class MyClass
{
    [MyAttribute]
    [YourAttribute]
    public virtual void MyMethod() 
    {
        //...
    }
}
[Visual Basic]' In Microsoft Visual Basic .NET, you apply multiple attributes
' by separating them with commas.
Public Class [MyClass]
    <MyAttribute, YourAttribute> Public Overridable Sub MyMethod()
        '...
    End Sub
End Class

Finally, the class YourClass is inherited from the base class MyClass. The method MyMethod shows MyAttribute, but not YourAttribute.

public class YourClass: MyClass
{
      //MyMethod will have MyAttribute but not YourAttribute.
      public override void MyMethod()
      {
         //...
      }

}
[Visual Basic]Public Class YourClass
   Inherits [MyClass]
      'MyMethod will have MyAttribute but not YourAttribute.
      Public overrides Sub MyMethod()
         '...
      End Sub
End Class

AllowMultiple Property

The AllowMultiple property indicates whether multiple instances of your attribute can exist on an element. If set to true, multiple instances are allowed; if set to false (the default), only one instance is allowed.

In the following code example, MyAttribute has a default AllowMultiple value of false, while YourAttribute has a value of true.

//This defaults to AllowMultiple = false.
public class MyAttribute :Attribute
{
}

[AttributeUsage(AllowMultiple = true)]
public class YourAttribute : Attribute
{
}
[Visual Basic]'This defaults to AllowMultiple = false.
<AttributeUsage(AttributeTargets.Method)> Public Class _
MyAttribute
    Inherits Attribute
End Class

<AttributeUsage(AttributeTargets.Method, AllowMultiple := True)> Public Class _
YourAttribute
    Inherits Attribute
End Class

When multiple instances of these attributes are applied, MyAttribute produces a compiler error. The following code example shows the valid use of YourAttribute and the invalid use of MyAttribute.

public class MyClass
{
    //This produces an error.
    //Duplicates are not allowed.
    [MyAttribute]
    [MyAttribute]
    public void MyMethod() {
        //...
    }

    //This is valid.
    [YourAttribute]
    [YourAttribute]
    public void YourMethod(){
    //...
    }
}
[Visual Basic]' In Microsoft Visual Basic .NET you apply multiple attributes
' by separating them with commas.
Public Class [MyClass]
    'This produces an error.
    'Duplicates are not allowed.
    <MyAttribute, MyAttribute> Public Overridable Sub MyMethod()
        '...
    End Sub

    'This is valid.    
    <YourAttribute, YourAttribute> Public Sub YourMethod()
        '...
    End Sub
End Class

If both the AllowMultiple property and the Inherited property are set to true, a class that is inherited from another class can inherit an attribute and have another instance of the same attribute applied in the same child class. If AllowMultiple is set to false, the values of any attributes in the parent class will be overwritten by new instances of the same attribute in the child class.

Declaring the Attribute Class

After you apply the AttributeUsageAttribute, you can begin to define the specifics of your attribute. The declaration of an attribute class looks similar to the declaration of a traditional class, as demonstrated by the following code.

public class MyAttribute : System.Attribute 
{
    // . . . 
}
[Visual Basic]' This attribute is only usable with methods
<AttributeUsage(AttributeTargets.Method)> Public Class MyAttribute
    Inherits System.Attribute
    ' . . . 
End Class

This attribute definition demonstrates the following points:

  • Attribute classes must be declared as public classes.
  • By convention, the name of the attribute class ends with the word Attribute. While not required, this convention is recommended for readability. When the attribute is applied, the inclusion of the word Attribute is optional.
  • All attribute classes must inherit directly or indirectly from System.Attribute.
  • In Microsoft Visual Basic, all custom attribute classes must have the AttributeUsageAttribute attribute.

Declaring Constructors

Attributes are initialized with constructors in the same way as traditional classes. The following code fragment illustrates a typical attribute constructor. This public constructor takes a parameter and sets its value equal to a member variable.

public MyAttribute(bool myvalue)
{
    this.myvalue = myvalue;        
}
[Visual Basic]Public Sub New(newvalue As Boolean)
    Me.myvalue = newvalue
End Sub

You can overload the constructor to accommodate different combinations of values. If you also define a property for your custom attribute class, you can use a combination of named and positional parameters when initializing the attribute. Typically, you define all required parameters as positional and all optional parameters as named. In this case, the attribute cannot be initialized without the required parameter. All other parameters are optional. Note that in Visual Basic, constructors for an attribute class should not use a ParamArray argument.

The following code example shows how an attribute that uses the previous constructor can be applied using optional and required parameters. It assumes that the attribute has one required Boolean value and one optional string property.

//One required (positional) and one optional (named) parameter are applied.
[MyAttribute(false, OptionalParameter = "optional data")]
//One required (positional) parameter is applied.
[MyAttribute(false)]
[Visual Basic]'One required (positional) and one optional (named) parameter are applied.
<MyAttribute(False, OptionalParameter := "optional data")>
' ...
'One required (positional) parameter is applied.
<MyAttribute(False)>

Declaring Properties

If you want to define a named parameter or provide an easy way to return the values stored by your attribute, declare a property. Attribute properties should be declared as public entities with a description of the data type that will be returned. Define the variable that will hold the value of your property and associate it with the get and set methods. The following code example demonstrates how to implement a simple property in your attribute.

public bool MyProperty
{
    get {return this.myvalue;}
    set {this.myvalue = value;}
}
[Visual Basic]Public Property MyProperty As Boolean
    Get
        Return Me.myvalue
    End Get
    Set
        Me.myvalue = value
    End Set
End Property

Custom Attribute Example

This section incorporates the previous information and shows how to design a simple attribute that documents information about the author of a section of code. The attribute in this example stores the name and level of the programmer, and whether the code has been reviewed. It uses three private variables to store the actual values to save. Each variable is represented by a public property that gets and sets the values. Finally, the constructor is defined with two required parameters.

[AttributeUsage(AttributeTargets.All)]
public class DeveloperAttribute : System.Attribute 
{
        
    //Private fields.
    private string name;
    private string level;
    private bool reviewed;

    //This constructor defines two required parameters: name and level.

    public  DeveloperAttribute(string name,string level)
    {
        this.name = name;
        this.level = level; 
        this.reviewed = false;
    }

    //Define Name property.
    //This is a read-only attribute.
        
    public virtual string Name
    {
        get {return name;}        
    }

    //Define Level property.
    //This is a read-only attribute.
        
    public virtual string Level
    {
        get {return level;}
    }

    //Define Reviewed property. 
    //This is a read/write attribute. 

    public virtual bool Reviewed
    {
        get {return reviewed;}
    set {reviewed = value;}
    }
}
[Visual Basic]<AttributeUsage(AttributeTargets.All)> Public Class DeveloperAttribute
    Inherits System.Attribute
    
    'Private fields.
    Private m_name As String
    Private m_level As String
    Private m_reviewed As Boolean    
    
    'This constructor defines two required parameters: name and level.
    Public Sub New(name As String, level As String)
        Me.m_name = name
        Me.m_level = level
        Me.m_reviewed = False
    End Sub
    
    'Define Name property.
    'This is a read-only attribute.    
    Public Overridable ReadOnly Property Name() As String
        Get
            Return m_name
        End Get
    End Property 
    
    'Define Level property.
    'This is a read-only attribute.    
    Public Overridable ReadOnly Property Level() As String
        Get
            Return m_level
        End Get
    End Property
    
    'Define Reviewed property. 
    'This is a read/write attribute.    
    Public Overridable Property Reviewed() As Boolean
        Get
            Return m_reviewed
        End Get
        Set
            m_reviewed = value
        End Set
    End Property
End Class

You can apply this attribute using the full name, DeveloperAttribute, or using the abbreviated name, Developer, in one of the following ways.

[Developer("Joan Smith", "1")]
[Developer("Joan Smith", "1", Reviewed = true)]
[Visual Basic]<Developer("Joan Smith", "1")>
<Developer("Joan Smith", "1", Reviewed := True)>

The first example shows the attribute applied with only the required named parameters, while the second example shows the attribute applied with both the required and optional parameters.

See Also

Extending Metadata Using Attributes | System.Attribute | AttributeUsageAttribute Class