Click here to Skip to main content
Click here to Skip to main content

.NET Enum: The Next Level

By , 25 Jul 2006
 

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)

About the Author

mikeperetz
Web Developer
Canada Canada
Member
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.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralIt was only a good idea when ...memberKamp Huang24 Oct '09 - 18:07 
GeneralI think this is just excellentmemberEd Guzman3 Aug '09 - 4:34 
GeneralRe: I think this is just excellentmembermikeperetz1 Jan '10 - 6:57 
QuestionIs there a bug in the GetAttribute Method?memberMember 28994997 May '09 - 1:21 
AnswerRe: Is there a bug in the GetAttribute Method?membermikeperetz24 Mar '11 - 4:52 
NewsMoremembermikeperetz16 Nov '07 - 1:43 
GeneralRe: MoreadminChris Maunder28 Dec '07 - 10:08 
GeneralRe: Moremembermikeperetz4 Jan '08 - 18:01 
GeneralGood one!memberTechieFreak18 Oct '07 - 17:00 
QuestionFew Questions...member12R27 Feb '07 - 4:15 
QuestionWhat do you think about this solutionmember12R27 Feb '07 - 4:05 
GeneralGreat work again...memberantecedents30 Nov '06 - 13:41 
GeneralUsing Enum design patternmemberbeatles16922 Sep '06 - 22:52 
GeneralRe: Using Enum design patternmembermikeperetz28 Nov '06 - 15:06 
GeneralI use a simpler solution...memberTimothy Paul Narron28 Aug '06 - 13:54 
GeneralRe: I use a simpler solution...memberAnthony Missico27 Apr '08 - 10:50 
Generalgood ideamemberHossein eraghi1 Aug '06 - 21:06 
GeneralNot a good idea...memberVeilance19 Jul '06 - 5:12 
GeneralRe: Not a good idea...membermikeperetz19 Jul '06 - 7:23 
GeneralIt's an overkillmemberSpaider17 Jul '06 - 23:54 
AnswerRe: It's an overkillmembermikeperetz18 Jul '06 - 1:03 
Generalwhy not use [Regular Employee] in the enum statementmembercarl.herbert17 Jul '06 - 19:35 
GeneralUseful, I tied it to our Translator and it works for all coutrismembernaytron197517 Jul '06 - 12:43 
QuestionWhy not use a custom TypeConverter? [modified]memberSteve Mitcham14 Jul '06 - 7:16 
AnswerRe: Why not use a custom TypeConverter?membermikeperetz15 Jul '06 - 3:57 
AnswerRe: Why not use a custom TypeConverter?memberNathan Baulch31 Jul '06 - 17:12 
QuestionIs it possible to edit such in propertygrid?memberokcode13 Jul '06 - 16:29 
AnswerRe: Is it possible to edit such in propertygrid?membermikeperetz14 Jul '06 - 5:05 
GeneralEasy and Fastmemberrperetz13 Jul '06 - 11:12 
GeneralRe: Easy and Fastmemberma1achai18 Jul '06 - 14:48 
GeneralRe: Easy and Fastmemberantecedents30 Nov '06 - 13:44 

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

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