Click here to Skip to main content
11,479,841 members (46,846 online)
Click here to Skip to main content

.NET Enum: The Next Level

, 25 Jul 2006 CPOL 69K 97
Rate this:
Please Sign up or sign in to vote.
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

mikeperetz
Web Developer
Canada Canada
I am currently working as a team leader with a group of amazing .NET programmers. I love coding with .NET, and I love to apply design patterns into my work. Lately I had some free time, so I decided to write some articles, hoping I will spare someone frustration and anxiety.

Comments and Discussions

 
GeneralIt was only a good idea when ... Pin
Kamp Huang24-Oct-09 19:07
memberKamp Huang24-Oct-09 19:07 
GeneralI think this is just excellent Pin
Ed Guzman3-Aug-09 5:34
memberEd Guzman3-Aug-09 5:34 
GeneralRe: I think this is just excellent Pin
mikeperetz1-Jan-10 7:57
membermikeperetz1-Jan-10 7:57 
QuestionIs there a bug in the GetAttribute Method? Pin
Member 28994997-May-09 2:21
memberMember 28994997-May-09 2:21 
AnswerRe: Is there a bug in the GetAttribute Method? Pin
mikeperetz24-Mar-11 5:52
membermikeperetz24-Mar-11 5:52 
NewsMore Pin
mikeperetz16-Nov-07 2:43
membermikeperetz16-Nov-07 2:43 
GeneralRe: More Pin
Chris Maunder28-Dec-07 11:08
adminChris Maunder28-Dec-07 11:08 
GeneralRe: More Pin
mikeperetz4-Jan-08 19:01
membermikeperetz4-Jan-08 19:01 
GeneralGood one! Pin
TechieFreak18-Oct-07 18:00
memberTechieFreak18-Oct-07 18:00 
QuestionFew Questions... Pin
12R27-Feb-07 5:15
member12R27-Feb-07 5:15 
QuestionWhat do you think about this solution Pin
12R27-Feb-07 5:05
member12R27-Feb-07 5:05 
GeneralGreat work again... Pin
antecedents30-Nov-06 14:41
memberantecedents30-Nov-06 14:41 
GeneralUsing Enum design pattern Pin
beatles16922-Sep-06 23:52
memberbeatles16922-Sep-06 23:52 
GeneralRe: Using Enum design pattern Pin
mikeperetz28-Nov-06 16:06
membermikeperetz28-Nov-06 16:06 
GeneralI use a simpler solution... Pin
Timothy Paul Narron28-Aug-06 14:54
memberTimothy Paul Narron28-Aug-06 14:54 
GeneralRe: I use a simpler solution... Pin
Anthony Missico27-Apr-08 11:50
memberAnthony Missico27-Apr-08 11:50 
Generalgood idea Pin
Hossein eraghi1-Aug-06 22:06
memberHossein eraghi1-Aug-06 22:06 
GeneralNot a good idea... Pin
Veilance19-Jul-06 6:12
memberVeilance19-Jul-06 6:12 
GeneralRe: Not a good idea... Pin
mikeperetz19-Jul-06 8:23
membermikeperetz19-Jul-06 8:23 
GeneralIt's an overkill Pin
Spaider18-Jul-06 0:54
memberSpaider18-Jul-06 0:54 
AnswerRe: It's an overkill Pin
mikeperetz18-Jul-06 2:03
membermikeperetz18-Jul-06 2:03 
Generalwhy not use [Regular Employee] in the enum statement Pin
carl.herbert17-Jul-06 20:35
membercarl.herbert17-Jul-06 20:35 
GeneralUseful, I tied it to our Translator and it works for all coutris Pin
naytron197517-Jul-06 13:43
membernaytron197517-Jul-06 13:43 
QuestionWhy not use a custom TypeConverter? [modified] Pin
Steve Mitcham14-Jul-06 8:16
memberSteve Mitcham14-Jul-06 8:16 
AnswerRe: Why not use a custom TypeConverter? Pin
mikeperetz15-Jul-06 4:57
membermikeperetz15-Jul-06 4:57 
AnswerRe: Why not use a custom TypeConverter? Pin
Nathan Baulch31-Jul-06 18:12
memberNathan Baulch31-Jul-06 18:12 
QuestionIs it possible to edit such in propertygrid? Pin
okcode13-Jul-06 17:29
memberokcode13-Jul-06 17:29 
AnswerRe: Is it possible to edit such in propertygrid? Pin
mikeperetz14-Jul-06 6:05
membermikeperetz14-Jul-06 6:05 
GeneralEasy and Fast Pin
rperetz13-Jul-06 12:12
memberrperetz13-Jul-06 12:12 
GeneralRe: Easy and Fast Pin
ma1achai18-Jul-06 15:48
memberma1achai18-Jul-06 15:48 
GeneralRe: Easy and Fast Pin
antecedents30-Nov-06 14:44
memberantecedents30-Nov-06 14:44 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.150520.1 | Last Updated 25 Jul 2006
Article Copyright 2006 by mikeperetz
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid