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.
- Create a mapping between enums and strings (using arrays or hashtables).
- Using the enum
ToString()
as a key for a language resource file.
- 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:
EmployeeType employee = EmployeeType.ChainStoreManager;
FieldInfo fieldInfo = employee.GetType().GetField("ChainStoreManager");
object[] attribArray = fieldInfo.GetCustomAttributes(false);
EnumDescriptionAttribute attrib =
(EnumDescriptionAttribute)attribArray[0];
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 ManagerAttribute
s, 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
{
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);
}
}
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.