Revision: 21495
Updated Code
at December 14, 2009 18:44 by bryanlyman
Updated Code
using System; using System.Collections.Generic; using System.Text.RegularExpressions; namespace System { /// <summary> /// A comparer which can be assigned and have it's case sensitivity switched on the fly before a comparision is done. /// </summary> public class CaseComparer : IEqualityComparer<string> { public bool CaseSensitive = true; public CaseComparer() { } public CaseComparer(bool caseSensitive) { this.CaseSensitive = caseSensitive; } #region IEqualityComparer<string> Members public bool Equals(string x, string y) { if (CaseSensitive) return StringComparer.CurrentCulture.Equals(x, y); else return StringComparer.CurrentCultureIgnoreCase.Equals(x, y); } public int GetHashCode(string obj) { return obj.GetHashCode(); } #endregion } /// <summary> /// Fixes the "parsing an enum returns an uncertain object" problem which occurs with standard enum objects having the same numeric value. /// </summary> /// <typeparam name="EnumType">An Enum object type. All other types are ignored.</typeparam> public static class Enum<EnumType> { private static Dictionary<Type, Dictionary<string, Cast>> nameLists = new Dictionary<Type, Dictionary<string, Cast>>(); private static bool ValidType { get { return (typeof(EnumType).BaseType == typeof(global::System.Enum)); } } private static Regex rxStartsWithKeyWord = new Regex(@"^[0-9]|^abstract$|^as$|^base$|^bool$|^break$|^byte$|^case$|^catch$|^char$|^checked$|^class$|^const$|^continue$|^decimal$|^default$|^delegate$|^do$|^double$|^else$|^enum$|^event$|^explicit$|^extern$|^$false|^finally$|^fixed$|^float$|^for$|^foreach$|^goto$|^if$|^implicit$|^in$|^int$|^interface$|^internal$|^is$|^lock$|^long$|^namespace$|^new$|^null$|^object$|^operator$|^out$|^overrride$|^params$|^private$|^protected$|^public$|^readonly$|^ref$|^return$|^sbyte$|^sealed$|^short$|^sizeof$|^stackalloc$|^static$|^string$|^struct$|^switch$|^this$|^thorw$|^true$|^try$|^typeof$|^uint$|^ulong$|^unchecked$|^unsafe$|^ushort$|^using$|^virtual$|^volatile$|^void$|^while$", RegexOptions.Compiled); /// <summary> /// Fixes the "parsing an enum returns an uncertain object" problem which occurs with standard enum objects having the same numeric value. /// </summary> public sealed class Cast : IComparable, IFormattable, IConvertible { public readonly EnumType Enumeration; public readonly string RealName; public bool CaseSensitive = true; public static readonly Enum<EnumType>.Cast DefaultEnumeration = default(EnumType).ToString(); public Enum<EnumType>.Cast Default { get { return DefaultEnumeration; } } public bool IsDefault { get { return (Enumeration.Equals(DefaultEnumeration.Enumeration)); } } internal Cast(global::System.Enum enumeration, string realName) { this.Enumeration = (EnumType)(object)enumeration; this.RealName = realName; } #region IComparable Members int IComparable.CompareTo(object obj) { if (obj is global::System.Enum) return ((Enum)(object)this.Enumeration).CompareTo(obj); if (obj is Cast) return Decimal.Compare(Convert.ToInt64(Enumeration), Convert.ToInt64(((Cast)obj).Enumeration)); if (obj is string) return (CaseSensitive) ? StringComparer.CurrentCulture.Compare(RealName, obj) : StringComparer.CurrentCultureIgnoreCase.Compare(RealName, obj); return -1; } #endregion #region IFormattable Members public override string ToString() { return RealName; } public string ToString(string format, IFormatProvider formatProvider) { if (formatProvider != null) return RealName.ToString(formatProvider); return RealName; } #endregion #region IConvertible Members TypeCode IConvertible.GetTypeCode() { return ((Enum)(object)Enumeration).GetTypeCode(); } bool IConvertible.ToBoolean(IFormatProvider provider) { return Convert.ToBoolean(Enumeration); } byte IConvertible.ToByte(IFormatProvider provider) { return Convert.ToByte(Enumeration); } char IConvertible.ToChar(IFormatProvider provider) { return Convert.ToChar(Enumeration); } DateTime IConvertible.ToDateTime(IFormatProvider provider) { return Convert.ToDateTime(Enumeration); } decimal IConvertible.ToDecimal(IFormatProvider provider) { return Convert.ToDecimal(Enumeration); } double IConvertible.ToDouble(IFormatProvider provider) { return Convert.ToDouble(Enumeration); } short IConvertible.ToInt16(IFormatProvider provider) { return Convert.ToInt16(Enumeration); } int IConvertible.ToInt32(IFormatProvider provider) { return Convert.ToInt32(Enumeration); } long IConvertible.ToInt64(IFormatProvider provider) { return Convert.ToInt64(Enumeration); } sbyte IConvertible.ToSByte(IFormatProvider provider) { return Convert.ToSByte(Enumeration); } float IConvertible.ToSingle(IFormatProvider provider) { return Convert.ToSingle(Enumeration); } object IConvertible.ToType(Type conversionType, IFormatProvider provider) { return Enumeration.GetType(); } ushort IConvertible.ToUInt16(IFormatProvider provider) { return Convert.ToUInt16(Enumeration); } uint IConvertible.ToUInt32(IFormatProvider provider) { return Convert.ToUInt32(Enumeration); } ulong IConvertible.ToUInt64(IFormatProvider provider) { return Convert.ToUInt64(Enumeration); } string IConvertible.ToString(IFormatProvider provider) { return this.ToString(RealName, provider); } #endregion //underscore-decode name public string FriendlyName { get { if (RealName.Contains("_")) return RealName.Replace("___", ". ").Replace("__", ", ").Replace("_", " ").Trim(); return RealName; } } public static implicit operator Cast(string enumName) { return Parse(enumName); } public static implicit operator EnumType(Cast enumerationCast) { if (enumerationCast != null) return enumerationCast.Enumeration; return DefaultEnumeration.Enumeration; } } /// <summary> /// Fixes the "parsing an enum returns an uncertain object" problem which occurs with standard enum objects having the same numeric value. /// Parse casts a enum name or Cast.FriendlyName to an Enum.Cast object of the proper enum type. /// </summary> /// <param name="enumName">The enumeration name to parse and cast to.</param> /// <param name="ignoreCase">Specifies if the parse should find and cast even if the string case is not exact.</param> /// <returns>An Enum.Cast object if the the generic type specified is a valid enum type. Otherwise returns null.</returns> public static Cast Parse(string enumName, bool ignoreCase) { if (ValidType) { Type enumType = typeof(EnumType); Dictionary<string, Cast> nameList; //cache or recieve from cache enum name list if (nameLists.ContainsKey(enumType)) nameList = nameLists[enumType]; else { nameList = new Dictionary<string, Cast>(new CaseComparer()); EnumType[] values = (EnumType[])global::System.Enum.GetValues(enumType); string[] names = global::System.Enum.GetNames(enumType); //store true values for (int i = 0; i < names.Length; i++) { EnumType trueValue = values[i]; Cast cacheItem = new Cast((Enum)(object)trueValue, names[i]); nameList.Add(names[i], cacheItem); } //store enum true name list associated with the enum type nameLists.Add(enumType, nameList); } //set string compare method of list CaseComparer comparer = (CaseComparer)nameList.Comparer; comparer.CaseSensitive = !ignoreCase; string fixedName = enumName.Trim(); //underscore-encode name if (fixedName.Contains(".") || fixedName.Contains(",") || fixedName.Contains(" ")) fixedName = fixedName.Replace(". ", "___").Replace(".", "___").Replace(", ", "__").Replace(",", "__").Replace(" ", "_").Trim(); //allow enumerations that start with a number or are a keyword, as long as it is preceeded by a single underscore if (rxStartsWithKeyWord.Match(fixedName).Success) fixedName = "_" + fixedName; if (nameList.ContainsKey(fixedName)) return nameList[fixedName]; return Enum<EnumType>.Cast.DefaultEnumeration; } return null; } /// <summary> /// Fixes the "parsing an enum returns an uncertain object" problem which occurs with standard enum objects having the same numeric value. /// Parse (case sensitive) casts a enum name or Cast.FriendlyName to an Enum.Cast object of the proper enum type. /// </summary> /// <param name="enumName">The enumeration name to parse and cast to.</param> /// <returns>An Enum.Cast object if the the generic type specified is a valid enum type. Otherwise returns null.</returns> public static Cast Parse(string enumName) { return Parse(enumName, false); } public static IList<EnumType> GetValues() { IList<EnumType> list = new List<EnumType>(); foreach (object value in Enum.GetValues(typeof(EnumType))) { list.Add((EnumType)value); } return list; } } } /* Syntax to use the class, is shown in the following advanced example: */ public class MyClass { [DefaultValue(eOperation.Custom)] public enum eOperation { Custom=0, create, inspect, edit, source } private string operationField; private Enum<eOperation>.Cast operationEnum; public eOperation operation { get { return this.operationEnum; } set { this.operationText = value.ToString(); } } public string operationText { get { return this.operationField; } set { this.operationEnum = Enum<eOperation>.Parse(value, true); if (this.operationEnum.IsDefault) this.operationField = value; else this.operationField = this.operationEnum.FriendlyName; } } } /* A direct assignment could also be used in the operationText (set) property, but the static Parse() function was used instead to display what is occurring behind the scenes with the implicit operator. Also, the Parse function is used to show how to use the case-insensitive Parse(), since the implicit operator is always case-sensitive. */
Revision: 21494
Updated Code
at December 14, 2009 18:22 by bryanlyman
Updated Code
using System; using System.Collections.Generic; using System.Text.RegularExpressions; namespace System { /// <summary> /// A comparer which can be assigned and have it's case sensitivity switched on the fly before a comparision is done. /// </summary> public class CaseComparer : IEqualityComparer<string> { public bool CaseSensitive = true; public CaseComparer() { } public CaseComparer(bool caseSensitive) { this.CaseSensitive = caseSensitive; } #region IEqualityComparer<string> Members public bool Equals(string x, string y) { if (CaseSensitive) return StringComparer.CurrentCulture.Equals(x, y); else return StringComparer.CurrentCultureIgnoreCase.Equals(x, y); } public int GetHashCode(string obj) { return obj.GetHashCode(); } #endregion } /// <summary> /// Fixes the "parsing an enum returns an uncertain object" problem which occurs with standard enum objects having the same numeric value. /// </summary> /// <typeparam name="EnumType">An Enum object type. All other types are ignored.</typeparam> public static class Enum<EnumType> { private static Dictionary<Type, Dictionary<string, Cast>> nameLists = new Dictionary<Type, Dictionary<string, Cast>>(); private static bool ValidType { get { return (typeof(EnumType).BaseType == typeof(global::System.Enum)); } } private static Regex rxStartsWithKeyWord = new Regex(@"^[0-9]|^abstract$|^as$|^base$|^bool$|^break$|^byte$|^case$|^catch$|^char$|^checked$|^class$|^const$|^continue$|^decimal$|^default$|^delegate$|^do$|^double$|^else$|^enum$|^event$|^explicit$|^extern$|^$false|^finally$|^fixed$|^float$|^for$|^foreach$|^goto$|^if$|^implicit$|^in$|^int$|^interface$|^internal$|^is$|^lock$|^long$|^namespace$|^new$|^null$|^object$|^operator$|^out$|^overrride$|^params$|^private$|^protected$|^public$|^readonly$|^ref$|^return$|^sbyte$|^sealed$|^short$|^sizeof$|^stackalloc$|^static$|^string$|^struct$|^switch$|^this$|^thorw$|^true$|^try$|^typeof$|^uint$|^ulong$|^unchecked$|^unsafe$|^ushort$|^using$|^virtual$|^volatile$|^void$|^while$", RegexOptions.Compiled); /// <summary> /// Fixes the "parsing an enum returns an uncertain object" problem which occurs with standard enum objects having the same numeric value. /// </summary> public sealed class Cast : IComparable, IFormattable, IConvertible { public readonly EnumType Enumeration; public readonly string RealName; public bool CaseSensitive = true; public static readonly Enum<EnumType>.Cast DefaultEnumeration = default(EnumType).ToString(); public Enum<EnumType>.Cast Default { get { return DefaultEnumeration; } } public bool IsDefault { get { return (Enumeration.Equals(DefaultEnumeration.Enumeration)); } } internal Cast(global::System.Enum enumeration, string realName) { this.Enumeration = (EnumType)(object)enumeration; this.RealName = realName; } #region IComparable Members int IComparable.CompareTo(object obj) { if (obj is global::System.Enum) return ((Enum)(object)this.Enumeration).CompareTo(obj); if (obj is Cast) return Decimal.Compare(Convert.ToInt64(Enumeration), Convert.ToInt64(((Cast)obj).Enumeration)); if (obj is string) return (CaseSensitive) ? StringComparer.CurrentCulture.Compare(RealName, obj) : StringComparer.CurrentCultureIgnoreCase.Compare(RealName, obj); return -1; } #endregion #region IFormattable Members public override string ToString() { return RealName; } public string ToString(string format, IFormatProvider formatProvider) { if (formatProvider != null) return RealName.ToString(formatProvider); return RealName; } #endregion #region IConvertible Members TypeCode IConvertible.GetTypeCode() { return ((Enum)(object)Enumeration).GetTypeCode(); } bool IConvertible.ToBoolean(IFormatProvider provider) { return Convert.ToBoolean(Enumeration); } byte IConvertible.ToByte(IFormatProvider provider) { return Convert.ToByte(Enumeration); } char IConvertible.ToChar(IFormatProvider provider) { return Convert.ToChar(Enumeration); } DateTime IConvertible.ToDateTime(IFormatProvider provider) { return Convert.ToDateTime(Enumeration); } decimal IConvertible.ToDecimal(IFormatProvider provider) { return Convert.ToDecimal(Enumeration); } double IConvertible.ToDouble(IFormatProvider provider) { return Convert.ToDouble(Enumeration); } short IConvertible.ToInt16(IFormatProvider provider) { return Convert.ToInt16(Enumeration); } int IConvertible.ToInt32(IFormatProvider provider) { return Convert.ToInt32(Enumeration); } long IConvertible.ToInt64(IFormatProvider provider) { return Convert.ToInt64(Enumeration); } sbyte IConvertible.ToSByte(IFormatProvider provider) { return Convert.ToSByte(Enumeration); } float IConvertible.ToSingle(IFormatProvider provider) { return Convert.ToSingle(Enumeration); } object IConvertible.ToType(Type conversionType, IFormatProvider provider) { return Enumeration.GetType(); } ushort IConvertible.ToUInt16(IFormatProvider provider) { return Convert.ToUInt16(Enumeration); } uint IConvertible.ToUInt32(IFormatProvider provider) { return Convert.ToUInt32(Enumeration); } ulong IConvertible.ToUInt64(IFormatProvider provider) { return Convert.ToUInt64(Enumeration); } string IConvertible.ToString(IFormatProvider provider) { return this.ToString(RealName, provider); } #endregion //underscore-decode name public string FriendlyName { get { if (RealName.Contains("_")) return RealName.Replace("___", ". ").Replace("__", ", ").Replace("_", " ").Trim(); return RealName; } } public static implicit operator Cast(string enumName) { return Parse(enumName); } public static implicit operator EnumType(Cast enumerationCast) { if (enumerationCast != null) return enumerationCast.Enumeration; return DefaultEnumeration.Enumeration; } } /// <summary> /// Fixes the "parsing an enum returns an uncertain object" problem which occurs with standard enum objects having the same numeric value. /// Parse casts a enum name or Cast.FriendlyName to an Enum.Cast object of the proper enum type. /// </summary> /// <param name="enumName">The enumeration name to parse and cast to.</param> /// <param name="ignoreCase">Specifies if the parse should find and cast even if the string case is not exact.</param> /// <returns>An Enum.Cast object if the the generic type specified is a valid enum type. Otherwise returns null.</returns> public static Cast Parse(string enumName, bool ignoreCase) { if (ValidType) { Type enumType = typeof(EnumType); Dictionary<string, Cast> nameList; //cache or recieve from cache enum name list if (nameLists.ContainsKey(enumType)) nameList = nameLists[enumType]; else { nameList = new Dictionary<string, Cast>(new CaseComparer()); EnumType[] values = (EnumType[])global::System.Enum.GetValues(enumType); string[] names = global::System.Enum.GetNames(enumType); //store true values for (int i = 0; i < names.Length; i++) { EnumType trueValue = values[i]; Cast cacheItem = new Cast((Enum)(object)trueValue, names[i]); nameList.Add(names[i], cacheItem); } //store enum true name list associated with the enum type nameLists.Add(enumType, nameList); } //set string compare method of list CaseComparer comparer = (CaseComparer)nameList.Comparer; comparer.CaseSensitive = !ignoreCase; string fixedName = enumName.Trim(); //underscore-encode name if (fixedName.Contains(".") || fixedName.Contains(",") || fixedName.Contains(" ")) fixedName = fixedName.Replace(". ", "___").Replace(".", "___").Replace(", ", "__").Replace(",", "__").Replace(" ", "_").Trim(); //allow enumerations that start with a number or are a keyword, as long as it is preceeded by a single underscore if (rxStartsWithKeyWord.Match(fixedName).Success) fixedName = "_" + fixedName; if (nameList.ContainsKey(fixedName)) return nameList[fixedName]; return Enum<EnumType>.Cast.DefaultEnumeration; } return null; } /// <summary> /// Fixes the "parsing an enum returns an uncertain object" problem which occurs with standard enum objects having the same numeric value. /// Parse (case sensitive) casts a enum name or Cast.FriendlyName to an Enum.Cast object of the proper enum type. /// </summary> /// <param name="enumName">The enumeration name to parse and cast to.</param> /// <returns>An Enum.Cast object if the the generic type specified is a valid enum type. Otherwise returns null.</returns> public static Cast Parse(string enumName) { return Parse(enumName, false); } public static IList<EnumType> GetValues() { IList<EnumType> list = new List<EnumType>(); foreach (object value in Enum.GetValues(typeof(EnumType))) { list.Add((EnumType)value); } return list; } } } Syntax to use the class, is shown in the following advanced example: public class MyClass { [DefaultValue(eOperation.Custom)] public enum eOperation { Custom=0, create, inspect, edit, source } private string operationField; private Enum<eOperation>.Cast operationEnum; public eOperation operation { get { return this.operationEnum; } set { this.operationText = value.ToString(); } } public string operationText { get { return this.operationField; } set { this.operationEnum = Enum<eOperation>.Parse(value, true); if (this.operationEnum.IsDefault) this.operationField = value; else this.operationField = this.operationEnum.FriendlyName; } } } A direct assignment could also be used in the operationText (set) property, but the static Parse() function was used instead to display what is occurring behind the scenes with the implicit operator. Also, the Parse function is used to show how to use the case-insensitive Parse(), since the implicit operator is always case-sensitive.
Revision: 21493
Initial Code
Initial URL
Initial Description
Initial Title
Initial Tags
Initial Language
at December 14, 2009 18:03 by bryanlyman
Initial Code
using System; using System.Collections.Generic; using System.Reflection; using System.Text.RegularExpressions; namespace System { /// <summary> /// A comparer which can be assigned and have it's case sensitivity switched on the fly before a comparision is done. /// </summary> public class CaseComparer : IEqualityComparer<string> { public bool CaseSensitive = true; public CaseComparer() { } public CaseComparer(bool caseSensitive) { this.CaseSensitive = caseSensitive; } #region IEqualityComparer<string> Members public bool Equals(string x, string y) { if (CaseSensitive) return StringComparer.CurrentCulture.Equals(x, y); else return StringComparer.CurrentCultureIgnoreCase.Equals(x, y); } public int GetHashCode(string obj) { return obj.GetHashCode(); } #endregion } /// <summary> /// Fixes the "parsing an enum returns an uncertain object" problem which occurs with standard enum objects having the same numeric value. /// </summary> /// <typeparam name="EnumType">An Enum object type. All other types are ignored.</typeparam> public static class Enum<EnumType> { private static Dictionary<Type, Dictionary<string, Cast>> nameLists = new Dictionary<Type, Dictionary<string, Cast>>(); private static bool ValidType { get { return (typeof(EnumType).BaseType == typeof(global::System.Enum)); } } private static Regex rxStartsWithKeyWord = new Regex(@"^[0-9]|^abstract$|^as$|^base$|^bool$|^break$|^byte$|^case$|^catch$|^char$|^checked$|^class$|^const$|^continue$|^decimal$|^default$|^delegate$|^do$|^double$|^else$|^enum$|^event$|^explicit$|^extern$|^$false|^finally$|^fixed$|^float$|^for$|^foreach$|^goto$|^if$|^implicit$|^in$|^int$|^interface$|^internal$|^is$|^lock$|^long$|^namespace$|^new$|^null$|^object$|^operator$|^out$|^overrride$|^params$|^private$|^protected$|^public$|^readonly$|^ref$|^return$|^sbyte$|^sealed$|^short$|^sizeof$|^stackalloc$|^static$|^string$|^struct$|^switch$|^this$|^thorw$|^true$|^try$|^typeof$|^uint$|^ulong$|^unchecked$|^unsafe$|^ushort$|^using$|^virtual$|^volatile$|^void$|^while$", RegexOptions.Compiled); /// <summary> /// Fixes the "parsing an enum returns an uncertain object" problem which occurs with standard enum objects having the same numeric value. /// </summary> public sealed class Cast : IComparable, IFormattable, IConvertible { public readonly EnumType Enumeration; public readonly string RealName; public bool CaseSensitive = true; public static readonly Enum<EnumType>.Cast DefaultEnumeration = default(EnumType).ToString(); public Enum<EnumType>.Cast Default { get { return DefaultEnumeration; } } public bool IsDefault { get { return (Enumeration.Equals(DefaultEnumeration.Enumeration)); } } internal Cast(global::System.Enum enumeration, string realName) { this.Enumeration = (EnumType)(object)enumeration; this.RealName = realName; } #region IComparable Members int IComparable.CompareTo(object obj) { if (obj is global::System.Enum) return ((Enum)(object)this.Enumeration).CompareTo(obj); if (obj is Cast) return Decimal.Compare(Convert.ToInt64(Enumeration), Convert.ToInt64(((Cast)obj).Enumeration)); if (obj is string) return (CaseSensitive) ? StringComparer.CurrentCulture.Compare(RealName, obj) : StringComparer.CurrentCultureIgnoreCase.Compare(RealName, obj); return -1; } #endregion #region IFormattable Members public override string ToString() { return RealName; } public string ToString(string format, IFormatProvider formatProvider) { if (formatProvider != null) return RealName.ToString(formatProvider); return RealName; } #endregion #region IConvertible Members TypeCode IConvertible.GetTypeCode() { return ((Enum)(object)Enumeration).GetTypeCode(); } bool IConvertible.ToBoolean(IFormatProvider provider) { return Convert.ToBoolean(Enumeration); } byte IConvertible.ToByte(IFormatProvider provider) { return Convert.ToByte(Enumeration); } char IConvertible.ToChar(IFormatProvider provider) { return Convert.ToChar(Enumeration); } DateTime IConvertible.ToDateTime(IFormatProvider provider) { return Convert.ToDateTime(Enumeration); } decimal IConvertible.ToDecimal(IFormatProvider provider) { return Convert.ToDecimal(Enumeration); } double IConvertible.ToDouble(IFormatProvider provider) { return Convert.ToDouble(Enumeration); } short IConvertible.ToInt16(IFormatProvider provider) { return Convert.ToInt16(Enumeration); } int IConvertible.ToInt32(IFormatProvider provider) { return Convert.ToInt32(Enumeration); } long IConvertible.ToInt64(IFormatProvider provider) { return Convert.ToInt64(Enumeration); } sbyte IConvertible.ToSByte(IFormatProvider provider) { return Convert.ToSByte(Enumeration); } float IConvertible.ToSingle(IFormatProvider provider) { return Convert.ToSingle(Enumeration); } object IConvertible.ToType(Type conversionType, IFormatProvider provider) { return Enumeration.GetType(); } ushort IConvertible.ToUInt16(IFormatProvider provider) { return Convert.ToUInt16(Enumeration); } uint IConvertible.ToUInt32(IFormatProvider provider) { return Convert.ToUInt32(Enumeration); } ulong IConvertible.ToUInt64(IFormatProvider provider) { return Convert.ToUInt64(Enumeration); } string IConvertible.ToString(IFormatProvider provider) { return this.ToString(RealName, provider); } #endregion //underscore-decode name public string FriendlyName { get { if (RealName.Contains("_")) return RealName.Replace("___", ". ").Replace("__", ", ").Replace("_", " ").Trim(); return RealName; } } public static implicit operator Cast(string enumName) { return Parse(enumName); } public static implicit operator EnumType(Cast enumerationCast) { if (enumerationCast != null) return enumerationCast.Enumeration; return DefaultEnumeration.Enumeration; } } /// <summary> /// Fixes the "parsing an enum returns an uncertain object" problem which occurs with standard enum objects having the same numeric value. /// Parse casts a enum name or Cast.FriendlyName to an Enum.Cast object of the proper enum type. /// </summary> /// <param name="enumName">The enumeration name to parse and cast to.</param> /// <param name="ignoreCase">Specifies if the parse should find and cast even if the string case is not exact.</param> /// <returns>An Enum.Cast object if the the generic type specified is a valid enum type. Otherwise returns null.</returns> public static Cast Parse(string enumName, bool ignoreCase) { if (ValidType) { Type enumType = typeof(EnumType); Dictionary<string, Cast> nameList; //cache or recieve from cache enum name list if (nameLists.ContainsKey(enumType)) nameList = nameLists[enumType]; else { nameList = new Dictionary<string, Cast>(new CaseComparer()); EnumType[] values = (EnumType[])global::System.Enum.GetValues(enumType); string[] names = global::System.Enum.GetNames(enumType); //store true values for (int i = 0; i < names.Length; i++) { EnumType trueValue = values[i]; Cast cacheItem = new Cast((Enum)(object)trueValue, names[i]); nameList.Add(names[i], cacheItem); } //store enum true name list associated with the enum type nameLists.Add(enumType, nameList); } //set string compare method of list CaseComparer comparer = (CaseComparer)nameList.Comparer; comparer.CaseSensitive = !ignoreCase; string fixedName = enumName.Trim(); //underscore-encode name if (fixedName.Contains(".") || fixedName.Contains(",") || fixedName.Contains(" ")) fixedName = fixedName.Replace(". ", "___").Replace(".", "___").Replace(", ", "__").Replace(",", "__").Replace(" ", "_").Trim(); //allow enumerations that start with a number or are a keyword, as long as it is preceeded by a single underscore if (rxStartsWithKeyWord.Match(fixedName).Success) fixedName = "_" + fixedName; if (nameList.ContainsKey(fixedName)) return nameList[fixedName]; return Enum<EnumType>.Cast.DefaultEnumeration; } return null; } /// <summary> /// Fixes the "parsing an enum returns an uncertain object" problem which occurs with standard enum objects having the same numeric value. /// Parse (case sensitive) casts a enum name or Cast.FriendlyName to an Enum.Cast object of the proper enum type. /// </summary> /// <param name="enumName">The enumeration name to parse and cast to.</param> /// <returns>An Enum.Cast object if the the generic type specified is a valid enum type. Otherwise returns null.</returns> public static Cast Parse(string enumName) { return Parse(enumName, false); } public static IList<EnumType> GetValues() { IList<EnumType> list = new List<EnumType>(); foreach (object value in Enum.GetValues(typeof(EnumType))) { list.Add((EnumType)value); } return list; } } } Syntax to use the class, is shown in the following advanced example: public class MyClass { [DefaultValue(eOperation.Custom)] public enum eOperation { Custom=0, create, inspect, edit, source } private string operationField; private Enum<eOperation>.Cast operationEnum; public eOperation operation { get { return this.operationEnum; } set { this.operationText = value.ToString(); } } public string operationText { get { return this.operationField; } set { this.operationEnum = Enum<eOperation>.Parse(value, true); if (this.operationEnum.IsDefault) this.operationField = value; else this.operationField = this.operationEnum.FriendlyName; } } } A direct assignment could also be used in the operationText (set) property, but the static Parse() function was used instead to display what is occurring behind the scenes with the implicit operator. Also, the Parse function is used to show how to use the case-insensitive Parse(), since the implicit operator is always case-sensitive.
Initial URL
Initial Description
For .net 2.0+ The one limitation to enumerations is revealed when you try to reverse lookup an enumeration value using Enum.Parse(). The parse function will return an inconsistent enum object if ever there are two or more enumerations with the same numeric value. This class fixes that problem. Written as a system extension and using implicit casting, the process has been made extremely easy and made the syntax for the parse function even simpler. The process even allows enumeration names starting with a number or the name of a C# keyword as long as the name is preceded by an underscore. The implicit cast from an Enum object to a Enum<type>.Cast object has been deliberately left out to account for single directional assignment, which forces the class to be used properly. An Enum to Cast object lookup would defeat the whole purpose of the class if the implicit operator is used during runtime; for this purpose a user assignment operator of type String is supplied. This simply forces the user to use Cast = Enum.ToString() to parse to a correct object. The ToString() overload for a Cast object returns a Friendly name which replaces all underscores with spaces and even allows double underscores for commas and triple underscores for periods; for this reason, the implicit \"from string\" caster also converts from a friendly name to the proper Enum object. This makes it very handy for enumerating through a list of items for a combo or list box and converting back to the proper object by simply supplying the name of the list item.
Initial Title
Enum Limitation Fix Using a Generic Class and Implicit Casting
Initial Tags
class, object, extension
Initial Language
C#