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 }