Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

.NET Enum: The Next Level

0.00/5 (No votes)
25 Jul 2006 1  
How to associate an object to your enum value.

Introduction

In this article, I am going to explore enums a little further. I am not going to cover the basic information about enums (you can always refer to MSDN for details). My biggest beef with enum is that it only represents numeric data. It is flexible to represent different integral types, but it can not hold a string. Enums do support the ToString() function; however, with limitation (I will go into it in more details, later in the article). Basically, I want to be able to make my enumeration element be associated to more than just an integer value. To achieve this, I will use Reflection and Attributes, and try to keep my example as simple as possible.

Using ToString() with enums

Let's create a simple enumeration...

public enum EmployeeType
{
    RegularEmployee,
    StoreManager,
    ChainStoreManager,
    DepartmentManager,
    Supervisor
}

Based on this simple plain vanilla enumeration, we can get the following information: a String representation using ToString().

EmployeeType employee = EmployeeType.ChainStoreManager;
Console.WriteLine(employee.ToString());
Console.WriteLine(EmployeeType.ChainStoreManager.ToString());

The output is the same for both lines of code:

ChainStoreManager
ChainStoreManager

But, what if I wanted to get "Chain Store Manager" with spaces? You can not create an enum type that contains spaces, your code wouldn't compile. There are a lot of solutions to this problem.

  1. Create a mapping between enums and strings (using arrays or hashtables).
  2. Using the enum ToString() as a key for a language resource file.
  3. Use a little bit of Reflection.

I will explore option 3...

Using attributes with enums

In order to associate a string with an enumeration, I used Attributes. I will start with a simple example that will associate my enumeration with a string.

public class EnumDescriptionAttribute : Attribute
{
    private string m_strDescription;
    public EnumDescriptionAttribute(string strPrinterName)
    {
        m_strDescription = strPrinterName;
    }

    public string Description
    {
        get { return m_strDescription; }
    }
}

EnumDescriptionAttribute is a simple attribute that holds a string. The attribute has a single property to return the description. For now, I am going to keep it as simple as possible. Now that I have my description attribute, I will associate it with each enum element.

public enum EmployeeType
{
    [EnumDescription("Regular Employee")]
    RegularEmploye,
    [EnumDescription("Store Manager")]
    StoreManager,
    [EnumDescription("Chain Store Manager")]
    ChainStoreManager,
    [EnumDescription("Department Manager")]
    DepartmentManager,
    [EnumDescription("On Floor Supervisor")]
    Supervisor
}

Getting the attribute value from an enum

In order to get the attribute value from an enumeration, I have to use Reflection. Here is an example:

// setup the enum
EmployeeType employee = EmployeeType.ChainStoreManager;

// get the field informaiton
FieldInfo fieldInfo = employee.GetType().GetField("ChainStoreManager");

// get the attributes for the enum field
object[] attribArray = fieldInfo.GetCustomAttributes(false);

// cast the one and only attribute to EnumDescriptionAttribute
EnumDescriptionAttribute attrib =
    (EnumDescriptionAttribute)attribArray[0];

// write the description
console.WriteLine("Description: {0}", attrib.Description);

Output:

Chain Store Manager

The most important line of code is: FieldInfo fieldInfo = employee.GetType().GetField("ChainStoreManager");. Notice that I hardcoded the enum element name ChainStoreManager, but if you go back and look at the ToString() function, you can see that I could have used ToString() instead.

A Generic function to get the description

Notice, I have used ToString() on the enum...

public static string GetEnumDescription(Enum enumObj)
{
    FieldInfo fieldInfo =
        enumObj.GetType().GetField(enumObj.ToString());

    object[] attribArray = fieldInfo.GetCustomAttributes(false);
    if (attribArray.Length == 0)
        return String.Empty;
    else
    {
        EnumDescriptionAttribute attrib =
            attribArray[0] as EnumDescriptionAttribute;

        return attrib.Description;
    }
}

Let's associate more than just a string to our enum elements

So far, we only associated a description to our enumeration element. However, it is fully possible to associate any type of data to our enum. To show this, I am going to create a new enumeration (similar to the first one).

public enum ManagerType
{
    StoreManager,
    ChainManager,
    Superivor
}

I am also creating a new attribute class, ManagerAttribute, that inherits from EnumDescription, and provides two additional pieces of information (min. salary and max. salary).

public class ManagerAttribute : EnumDescriptionAttribute
{
    private int m_intMinSalary;
    private int m_intMaxSalary;
    public ManagerAttribute(string strDescription,
        int intMinSalary,
        int intMaxSalary) : base(strDescription)
    {
        m_intMinSalary = intMinSalary;
        m_intMaxSalary = intMaxSalary;
    }

    public ManagerAttribute(string strDescription)
           : base(strDescription)
    {

    }

    public int MinSalary
    {
        get {return m_intMinSalary;}
        set { m_intMinSalary = value; }
    }
    public int MaxSalary
    {
        get { return m_intMaxSalary;}
        set { m_intMaxSalary = value; }
    }
}

Now, I am going to associate a ManagerAttribute to each enum element. Notice that I am using the set properties within my ManagerAttributes, so the code is more readable:

public enum ManagerType
{
    [Manager("Store Manager", MinSalary=40000, MaxSalary=100000)]
    StoreManager,
    [Manager("Chain Manager", MinSalary=50000, MaxSalary=110000)]
    ChainManager,
    [Manager("Store Supervisor", MinSalary=30000, MaxSalary=50000)]
    Superivor
}

The next step is improve our generic function to allow us to get any type of EnumDescriptionAttribute; using Generics, this is easily done!

public static T GetAttribute<T>(Enum enumObj) where T : EnumDescriptionAttribute
{
    // get field informaiton for our enum element

    FieldInfo fieldInfo = enumObj.GetType().GetField(enumObj.ToString());

    // get all the attributes associated with our enum

    object[] attribArray = fieldInfo.GetCustomAttributes(false);

    if (attribArray.Length == 0)
        return default(T);
    else
    {
        // cast the attribute and return it

        T attrib = (T)attribArray[0];
        if (attrib != null)
            return attrib;
        else
            return default(T);
    }
}

Helper functions

So far, we have got two helper functions, one to obtain a simple string description of an enum, and the other, a more generic function to obtain any attribute that inherits from EnumDescriptionAttribute. You can add these helper functions to a class like EnumHelper; but, in this example, I decided to simply add them to the existing EnumDescriptionAttribute.

public class EnumDescriptionAttribute : Attribute
{
    private string m_strDescription;
    public EnumDescriptionAttribute(string strEnumDescription)
    {
        m_strDescription = strEnumDescription;
    }

    public string Description { get { return m_strDescription; } }

    public static string GetEnumDescription(Enum enumObj)
    {
        FieldInfo fieldInfo = enumObj.GetType().GetField(enumObj.ToString());
        object[] attribArray = fieldInfo.GetCustomAttributes(false);
        if (attribArray.Length == 0)
            return String.Empty;
        else
        {
            EnumDescriptionAttribute attrib = attribArray[0]
                as EnumDescriptionAttribute;

            return attrib.Description;
        }
    }
    public static T GetAttribute<T>(Enum enumObj)
        where T : EnumDescriptionAttribute
    {
        FieldInfo fieldInfo = enumObj.GetType().GetField(enumObj.ToString());
        object[] attribArray = fieldInfo.GetCustomAttributes(false);
        if (attribArray.Length == 0)
            return default(T);
        else
        {
            T attrib = (T)attribArray[0];
            if (attrib != null)
                return attrib;
            else
                return default(T);
        }
    }
}

Finally... Let's see it all together

To get a string description, we can simple do this:

string desc = EnumDescriptionAttribute.GetEnumDescription(EmployeeType.DepartmentManager);

To get a ManagerAttribute, we can do this:

ManagerAttribute manager =
    EnumDescriptionAttribute.GetAttribute<ManagerAttribute>(
    EmployeeType.DepartmentManager);

Console.WriteLine("Manager: {0}, Min Salary: {1}, Max Salary {2}",
    attrib.Description,
    manager.MinSalary,
    manager.MaxSalary);

Now, you can see that you can use attributes to associate additional information to an element of an enum.

Conclusion

Okay, so you can see that it is not hard to associate additional information to your enumeration element by simply using some attributes and Reflection. However, it is important to note that I do not believe attributes should replace common business objects. I simply wanted to show that an enum does not have to only represent an integral value. As you read in this article, you can associate your enum to a string or any other class. One of the nice advantages of using attributes is that it is easy to implement, and it makes the code readable. You can see how readable the ManagerType enumeration is with the implementation of the ManagerAttribute. It is easy to know exactly what each enumeration item contains. I hope you enjoyed reading this article, and I welcome all comments or questions about it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here