Return to Snippet

Revision: 21495
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
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
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#