Creating And Using Custom Attributes In C#

Tools Used

Visual C# .NET

What are Attributes?

Here is a quote about what an attribute is all about:

"Attributes provide a powerful method of associating declarative information with C# code (types, methods, properties, and so forth). Once associated with a program entity, the attribute can be queried at run time and used in any number of ways." - MSDN

Attributes are classes that allow you to add additional information to elements of your class structure.

The information from the attribute can later be obtained by using Reflection. See the article on reflection on this site. In this article, we will show you how to add stereotypes to your method. A stereotype is a way to describe a specific category for a design element and is a term that comes from UML. Our stereotype attribute provides two strings to describe our methods, a stereotype string and a description string.

Below is the code for declaring our custom attribute, stereotype,

using System;  
using System.Reflection;  
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Constructor)]  
public class Stereotype: System.Attribute {  
    public string TheStereotypeName = "General";  
    public string TheDescription = "Method Info";  
    public Stereotype() {}  
    public Stereotype(string aStereotype, string aDescription) {  
        TheStereotypeName = aStereotype;  
        Description = aDescription;  
    }  
}

Every class requires two things to make it an attribute.

  1. An AttributeUsage Attribute to declare the class as an attribute.
  2. Derive from System.Attribute or some other attribute ultimately derived from System.Attribute.

    Below is the attribute usage from our example allowing our attribute to be associated with a class or a method or a constructor of a class,

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Constructor)] 

This element before the class takes 3 possible parameters.

Parameter Description Default Example
AttributeTargets What element of the class the attribute is targetting AttributeTargets.All the sample attribute targets classes constructors, and methods (see above)
IsAttributeMultiUse Can the attribute be set multiple times. false [Conditional("A"),Conditional("B")]
IsAttributeInherited The attribute can be inherited from derived classes. false  

To use our attribute, we simply place it above a method, and call the attribute's constructor. Below is an example of the Stereotype Attribute being used in a class called Customer with methods GetName and SetName. For simplicity, only the methods are displayed below,

// method 1 of our Customer class   
[Stereotype("Accessor", "Retrieve the name of the customer")]  
public string GetName() {  
    return Name;  
}  
// Method 2 of our customer class  
[Stereotype("Modifier", "Set the name of the customer")]  
public void SetName(string aName) {  
    Name = aName;  
}

Now we'll add a method to our stereotype attribute class that will allow us to retrieve the attribute information from the customer class and write it to the console. The method uses reflection and calls GetCustomAttributes on each item in our methodinfo collection to get a hold of the stereotype attributes of our class,

public static void WriteEachMethodInClass(Type t) // method of the Stereotype class  
{  
    System.Console.WriteLine("Methods for Class {0}", t.Name); // write class name to console  
    MethodInfo[] TheMethods = t.GetMethods(); // Get all the methods from the class using reflection  
    for (int i = 0; i < TheMethods.GetLength(0); i++) // cycle through each method and see if it has an attribute  
    {  
        MethodInfo mi = TheMethods[i];  
        object[] attrs = mi.GetCustomAttributes(); // Get collection of custom attributes from the method  
        foreach(Attribute attr in attrs) // get each attribute using foreach  
        {  
            if (attr is Stereotype) // is the attribute a Stereotype Attribute?  
            {  
                Stereotype theStereotype = (Stereotype) attr; // it is, write the Stereotype and the Description to the console  
                System.Console.WriteLine("{0} - {1}, {2}", mi.Name, theStereotype.TheStereotype, theStereotype.Description);  
            }  
        }  
    }  
    ConstructorInfo[] TheConstructors = t.GetConstructors(); // do the same for the constructors of our class  
    for (int i = 0; i < TheConstructors.GetLength(0); i++) {  
        ConstructorInfo mi = TheConstructors[i];  
        object[] attrs = mi.GetCustomAttributes();  
        foreach(Attribute attr in attrs) {  
            if (attr is Stereotype) {  
                Stereotype theStereotype = (Stereotype) attr;  
                System.Console.WriteLine("{0} - {1},{2}", mi.Name, theStereotype.TheStereotype, theStereotype.Description);  
            }  
        }  
    }  
} 

Here is an example of how we call our WriteEachMethodInClass to write out the stereotype attributes,  

public static int Main(string[] args) {  
    Stereotype.WriteEachMethodInClass(typeof(Account)); // Write the stereotype information of Account to the console.  
    Stereotype.WriteEachMethodInClass(typeof(Customer)); // Write the stereotype information of Customer to the console.  
    System.Console.WriteLine("Hit ENTER to continue...");  
    System.Console.Read();  
    return 0;  
}

Attributes can be used from everything to detailed code documentation, to version control. Look for future articles on this subject coming to C-Sharp Corner.