Enums are just great, but not very user friendly once you get past simple one word names. In this article I will show you how to make them far more usable via Attributes. We will be able to give them a user friendly name, parse strings to enum based on the enum’s value, member description, and user friendly description, and created orders lists and dictionaries.
public enum enRates
{
Main = 0, //Hourly
OffSiteFullDay = 22, //Off Site Full Day
OnSiteFullDay = 21, //On Site Full Day
OffSite = 202, //Off Site Hourly
OnSite = 201, //On Site Hourly
}
Notice how the values are not orders the way I want them and the names don’t match the user descriptions.
Now lets see what we can do by extending them through attributes:
public enum enRates
{
[EnumInformation("Hourly", true, 1)] Main = 0,
[EnumInformation("Off Site Full Day", true, 4)] OffSiteFullDay = 22,
[EnumInformation("On Site Full Day", true, 5)] OnSiteFullDay = 21,
[EnumInformation("Off Site Hourly", true, 2)] OffSite = 202,
[EnumInformation("On Site Hourly", true, 3)] OnSite = 201,
}
Now lets see what we can do with these attributes
a. Get a user friendly description for an enum
Console.WriteLine("Enum<enRates>.Description(enRates.Main) = {0}", Enum<enRates>.Description(enRates.Main));
gets:
Enum<enRates>.Description(enRates.Main) = Hourly
b. Parse values to get enum
Console.WriteLine("Enum<enRates>.Parse(\"Main\", enRates.OnSiteFullDay) = {0}", Enum<enRates>.Parse("Main", enRates.OnSiteFullDay));
Console.WriteLine("Enum<enRates>.Parse(\"Hourly\", enRates.OnSiteFullDay) = {0}", Enum<enRates>.Parse("Hourly", enRates.OnSiteFullDay));
Console.WriteLine("Enum<enRates>.Parse(\"0\", enRates.OnSiteFullDay) = {0}", Enum<enRates>.Parse("0", enRates.OnSiteFullDay));
Console.WriteLine("Enum<enRates>.Parse(\"On Site Full Day\", enRates.OnSiteFullDay) = {0}", Enum<enRates>.Parse("On Site Full Day", enRates.OnSiteFullDay));
Console.WriteLine("Enum<enRates>.Parse(\"OnSiteFullDay\", enRates.OnSiteFullDay) = {0}", Enum<enRates>.Parse("OnSiteFullDay", enRates.OnSiteFullDay));
Console.WriteLine("Enum<enRates>.Parse(\"UnKnown\", enRates.OnSiteFullDay) = {0}", Enum<enRates>.Parse("UnKnown", enRates.OnSiteFullDay));
gets:
Enum.Parse("Main", enRates.OnSiteFullDay) = Main
Enum.Parse("Hourly", enRates.OnSiteFullDay) = Main
Enum.Parse("0", enRates.OnSiteFullDay) = Main
Enum.Parse("On Site Full Day", enRates.OnSiteFullDay) = OnSiteFullDay
Enum.Parse("OnSiteFullDay", enRates.OnSiteFullDay) = OnSiteFullDay
Enum.Parse("UnKnown", enRates.OnSiteFullDay) = OnSiteFullDay
c. Get an ordered list for an enum (notice the order its ordered by the third value in the attribute, not the value of the member)
Console.WriteLine("ToList:");
List<enRates> list = Enum<enRates>.ToList();
foreach (enRates rate in list)
{
Console.WriteLine(" {0}", rate);
}
gets:
ToList:
Main
OffSite
OnSite
OffSiteFullDay
OnSiteFullDay
d. Get an ordered user friendly list of descriptions for an enum
Console.WriteLine("ToStringList:");
List<string> stringList = Enum<enRates>.ToStringList();
foreach (string rate in stringList)
{
Console.WriteLine(" {0}", rate);
}
gets:
ToStringList:
Hourly
Off Site Hourly
On Site Hourly
Off Site Full Day
On Site Full Day
e. Get an ordered user friendly dictionary with the member string value and user friendly value
Dictionary<string, string> stringDictionary = Enum<enRates>.ToStringDictionary();
foreach (KeyValuePair<string, string> rate in stringDictionary)
{
Console.WriteLine(" {0} = {1}", rate.Key, rate.Value);
}
gets:
ToStringDictionary:
Main = Hourly
OffSite = Off Site Hourly
OnSite = On Site Hourly
OffSiteFullDay = Off Site Full Day
OnSiteFullDay = On Site Full Day
f. Get an ordered user friendly dictionary with the int value of the member and user friendly value
Dictionary<int, string> dictionary = Enum<enRates>.ToDictionary();
foreach (KeyValuePair<int, string> rate in dictionary)
{
Console.WriteLine(" {0} = {1}", rate.Key, rate.Value);
}
gets:
ToDictionary:
0 = Hourly
202 = Off Site Hourly
201 = On Site Hourly
22 = Off Site Full Day
21 = On Site Full Day
g. Get an ordered user friendly dictionary with the member and user friendly value
Dictionary<enRates, string> valueDictionary = Enum<enRates>.ToValueDictionary();
foreach (KeyValuePair<enRates, string> rate in valueDictionary)
{
Console.WriteLine(" {0} = {1}", rate.Key, rate.Value);
}
gets:
ToValueDictionary:
Main = Hourly
OffSite = Off Site Hourly
OnSite = On Site Hourly
OffSiteFullDay = Off Site Full Day
OnSiteFullDay = On Site Full Day
Here’s the source code:
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
public class EnumInformationAttribute : Attribute
{
public string Description { get; internal set; }
public bool Active { get; internal set; }
public int? Order { get; internal set; }
public EnumInformationAttribute(string description, bool active, int? order)
{
Description = description;
Active = active;
Order = order;
}
public EnumInformationAttribute(string description, bool active, int order) : this(description, active, (int?)order) { }
public EnumInformationAttribute(string description, bool active) : this(description, active, null) { }
public EnumInformationAttribute(string description) : this(description, true, null) { }
}
public class EnumParseException : Exception
{
public EnumParseException() : base() { }
public EnumParseException(string message) : base(message) { }
public EnumParseException(string message, Exception innerException) : base(message, innerException) { }
}
public static class Enum<T>
{
#region EnumInformation
public class EnumInformation
{
public T Value { get; internal set; }
public string DefaultDescription { get; internal set; }
public string WithSpaces { get; internal set; }
public bool Active { get; internal set; }
public int Order { get; internal set; }
internal EnumInformation(T value, string defaultDescription, string withSpaces, bool active, int order)
{
Value = value;
DefaultDescription = defaultDescription;
WithSpaces = withSpaces;
Active = active;
Order = order;
}
internal EnumInformation(T value) : this(value, value.ToString(), RemoveTitleCase(value.ToString()), true, Convert.ToInt32(value)) { }
}
#endregion
#region Static Constructor
internal static List<EnumInformation> _List = null;
internal static Dictionary<T, EnumInformation> _Descriptions = null;
internal static Dictionary<string, T> _LookupValues = null;
static Enum()
{
string key;
Type type = typeof(T);
EnumInformationAttribute[] enumInformationAttributes = null;
EnumInformation enumInformation = null;
_List = new List<EnumInformation>();
_Descriptions = new Dictionary<T, EnumInformation>();
_LookupValues = new Dictionary<string, T>();
if (type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) type = (new System.ComponentModel.NullableConverter(type)).UnderlyingType;
if (!(type.IsEnum)) throw new Exception("Cannot create Enum<T> when T is not a Enum");
foreach (T value in Enum.GetValues(type))
{
enumInformation = new EnumInformation(value);
enumInformationAttributes = (EnumInformationAttribute[])(type.GetField(value.ToString()).GetCustomAttributes(typeof(EnumInformationAttribute), false));
if (enumInformationAttributes.Length > 0)
{
enumInformation.DefaultDescription = enumInformationAttributes[0].Description;
enumInformation.WithSpaces = enumInformationAttributes[0].Description;
enumInformation.Active = enumInformationAttributes[0].Active;
enumInformation.Order = enumInformationAttributes[0].Order ?? enumInformation.Order;
}
_List.Add(enumInformation);
_Descriptions.Add(value, enumInformation);
key = Convert.ToInt32(value).ToString().ToLower(); if (!_LookupValues.ContainsKey(key)) _LookupValues.Add(key, value);
key = enumInformation.Value.ToString().ToLower(); if (!_LookupValues.ContainsKey(key)) _LookupValues.Add(key, value);
key = enumInformation.DefaultDescription.ToLower(); if (!_LookupValues.ContainsKey(key)) _LookupValues.Add(key, value);
key = enumInformation.WithSpaces.ToLower(); if (!_LookupValues.ContainsKey(key)) _LookupValues.Add(key, value);
}
_List.Sort((x, y) => x.Order.CompareTo(y.Order));
}
#endregion
#region RemoveTitleCase
private static string RemoveTitleCase(string value)
{
string result = value;
result = System.Text.RegularExpressions.Regex.Replace(result, @"(?<begin>(\w*?))(?<end>[A-Z]+)", string.Format(@"${{begin}}{0}${{end}}", " ")).Trim();
return result;
}
#endregion
#region Description
public static string Description(T value)
{
return Description(value, true);
}
public static string Description(T value, bool addSpaceBetweenWords)
{
string result = string.Empty;
if (_Descriptions.ContainsKey(value)) result = (addSpaceBetweenWords ? _Descriptions[value].WithSpaces : _Descriptions[value].DefaultDescription);
return result;
}
#endregion
#region Count
public static int Count()
{
return _Descriptions.Count;
}
#endregion
#region Parse
public static T Parse(string value)
{
T result;
value = (value ?? string.Empty).ToLower();
if (_LookupValues.ContainsKey(value))
{
result = _LookupValues[value];
}
else
{
throw new EnumParseException(string.Format("No Match for \"{0}\" in \"{1}\"", value, typeof(T).Name));
}
return result;
}
public static T Parse(string value, T defaultValue)
{
T result;
value = (value ?? string.Empty).ToLower();
if (_LookupValues.ContainsKey(value))
result = _LookupValues[value];
else
result = defaultValue;
return result;
}
public static bool TryParse(string value, out T parsedValue)
{
bool result = false;
value = (value ?? string.Empty).ToLower();
if (_LookupValues.ContainsKey(value))
{
parsedValue = _LookupValues[value];
result = true;
}
else
{
parsedValue = default(T);
}
return result;
}
#endregion
#region ToList
public static List<T> ToList()
{
return ToList(false);
}
public static List<T> ToList(bool activeOnly)
{
List<T> result = new List<T>();
foreach (EnumInformation enumInformation in _List)
{
if (!activeOnly || enumInformation.Active) result.Add(enumInformation.Value);
}
return result;
}
#endregion
#region ToStringList
public static List<string> ToStringList()
{
return ToStringList(true);
}
public static List<string> ToStringList(bool addSpaceBetweenWords)
{
return ToStringList(addSpaceBetweenWords, false);
}
public static List<string> ToStringList(bool addSpaceBetweenWords, bool activeOnly)
{
List<string> result = new List<string>();
foreach (EnumInformation enumInformation in _List)
{
if (!activeOnly || enumInformation.Active) result.Add((addSpaceBetweenWords ? enumInformation.WithSpaces : enumInformation.DefaultDescription));
}
return result;
}
#endregion
#region ToStringDictionary
public static Dictionary<string, string> ToStringDictionary()
{
return ToStringDictionary(true);
}
public static Dictionary<string, string> ToStringDictionary(bool addSpaceBetweenWords)
{
return ToStringDictionary(addSpaceBetweenWords, false);
}
public static Dictionary<string, string> ToStringDictionary(bool addSpaceBetweenWords, bool useDescriptionAsKey)
{
return ToStringDictionary(addSpaceBetweenWords, useDescriptionAsKey, false);
}
public static Dictionary<string, string> ToStringDictionary(bool addSpaceBetweenWords, bool useDescriptionAsKey, bool activeOnly)
{
Dictionary<string, string> result = new Dictionary<string, string>();
foreach (EnumInformation enumInformation in _List)
{
string description = (addSpaceBetweenWords ? enumInformation.WithSpaces : enumInformation.DefaultDescription);
string key = (useDescriptionAsKey ? description : enumInformation.Value.ToString());
if (!activeOnly || enumInformation.Active) result.Add(key, description);
}
return result;
}
#endregion
#region ToDictionary
public static Dictionary<int, string> ToDictionary()
{
return ToDictionary(true);
}
public static Dictionary<int, string> ToDictionary(bool addSpaceBetweenWords)
{
return ToDictionary(addSpaceBetweenWords, false);
}
public static Dictionary<int, string> ToDictionary(bool addSpaceBetweenWords, bool activeOnly)
{
Dictionary<int, string> result = new Dictionary<int, string>();
foreach (EnumInformation enumInformation in _List)
{
if (!activeOnly || enumInformation.Active) result.Add(Convert.ToInt32(enumInformation.Value), (addSpaceBetweenWords ? enumInformation.WithSpaces : enumInformation.DefaultDescription));
}
return result;
}
#endregion
#region ToValueDictionary
public static Dictionary<T, string> ToValueDictionary()
{
return ToValueDictionary(true);
}
public static Dictionary<T, string> ToValueDictionary(bool addSpaceBetweenWords)
{
return ToValueDictionary(addSpaceBetweenWords, false);
}
public static Dictionary<T, string> ToValueDictionary(bool addSpaceBetweenWords, bool activeOnly)
{
Dictionary<T, string> result = new Dictionary<T, string>();
foreach (EnumInformation enumInformation in _List)
{
if (!activeOnly || enumInformation.Active) result.Add(enumInformation.Value, (addSpaceBetweenWords ? enumInformation.WithSpaces : enumInformation.DefaultDescription));
}
return result;
}
#endregion
}