Using Attributes with Enums

Attributes are a mechanism for adding metadata. Reflection is the process by which a program can read its own metadata. This article shows how to add attributes to an enum, and how to read out the attributes via Reflection.

An attribute is an object that represents the data that is associated with a program element. The element to which an attribute is attached is called the target of that attribute.

Attribute targets can be one of the following:  All, Assembly, Class, Constructor, Delegate, Enum, Event, Field, Interface, Method, Module, Parameter, Property, ReturnValue, and  Struct.  This gives us a lot of flexibility in how we can use attributes. This example will show how to add attributes to an enum, a technique that can be very useful in a range of paradigms.

The easiest way to illustrate the concept is to look at a "short but complete" example program:

using System;
using System.Reflection;
public enum Animals
{
    [AnimalWeight(1304)]
    [AnimalColor("Brown")]
    DONKEY = 1,
    [AnimalWeight(3250)]
    [AnimalColor("Grey")]
    ELEPHANT = 2
}
class AnimalWeightAttribute : Attribute
{
    private readonly int weight;
    public AnimalWeightAttribute(int weight) { this.weight = weight; }
    public override string ToString()
    {
        return "weight = " +
            weight.ToString();
    }
}
class AnimalColorAttribute : Attribute
{
    private readonly string color;
    public AnimalColorAttribute(string color) { this.color = color; }
    public override string ToString() 
    { return "Color = " +
            color.ToString(); 
    }
}
static class Program
{
    static void Main()
    {
        Type dataType = Enum.GetUnderlyingType(typeof(Animals));
        foreach (FieldInfo field in
typeof(Animals).GetFields(BindingFlags.Static | BindingFlags.GetField |
BindingFlags.Public))
        {
            object value = field.GetValue(null);
            Console.WriteLine("{0}={1}", field.Name,
Convert.ChangeType(value, dataType));
            foreach (Attribute attrib in
field.GetCustomAttributes(true))
            {
                Console.WriteLine("\t{0}", attrib);
            }
        }
        Console.WriteLine("Any key to quit.");
        Console.ReadLine();
    }
}

As can be seen above, we define our enum "Animals" and include the attribute names and values above each field of the enum, in the format:  [AttributeType(Value)]. The attribute(s) should be followed by the definition of the enum element and its optional value. Note that even though the Attribute classes are named "XXXAttribute" we can still use the shorthand "XXX" in the element decoration.

Just below the enum definition, we add a class representing each Attribute which derives from Attribute. The ToString override is to make display easier. In practice, a developer would put each of these in it's own separate class file.

Finally in the Main program, we get the underlying type of the enum, and then iterate the FieldInfo objects via Reflection. The inner foreach loop uses the Convert class ChangeType method, which returns an  Object with a specified type and whose value is equivalent to a specified object. Finally, the inner loop uses the GetCustomAttributes method to get an array of all the attributes. The boolean parameter specifies whether to search this member's inheritance chain to find the attributes.

Using this technique, you can store virtually any kind of metadata in an enum and read it out for use at runtime.  The sample Visual Studio 2008 Solution includes the complete console app. If you do not have VS 2008, you should be able to just load the .csproj file in an earlier version of Visual Studio.

By Peter Bromberg   Popularity  (24779 Views)