代码之家  ›  专栏  ›  技术社区  ›  Manish Basantani

如何对枚举值进行色组化?

  •  88
  • Manish Basantani  · 技术社区  · 15 年前

    我想编写一个函数,它可以根据 enum . 在匹配的情况下,它应该返回枚举实例;否则,它应该返回默认值。

    功能不能在内部使用 try / catch ,不包括使用 Enum.Parse ,在给定无效参数时引发异常。

    我想用一些符合 TryParse 实现此功能的函数:

    public static TEnum ToEnum<TEnum>(this string strEnumValue, TEnum defaultValue)
    {
       object enumValue;
       if (!TryParse (typeof (TEnum), strEnumValue, out enumValue))
       {
           return defaultValue;
       }
       return (TEnum) enumValue;
    }
    
    14 回复  |  直到 6 年前
        1
  •  31
  •   ethane    7 年前

    正如其他人所说,你必须实现你自己的 TryParse . Simon Mourier提供了一个全面的实现,它可以处理所有事情。

    如果使用位域枚举(即标志),还必须处理类似 "MyEnum.Val1|MyEnum.Val2" 它是两个枚举值的组合。如果你只是打电话 Enum.IsDefined 使用此字符串,它将返回false,即使 Enum.Parse 正确处理。

    更新

    正如丽莎和克里斯蒂安在评论中提到的, Enum.TryParse 现在可用于c_in.net4及更高版本。

    MSDN Docs

        2
  •  98
  •   Julien Roncaglia    11 年前

    Enum.IsDefined将完成任务。它的效率可能不如胰蛋白酶的效率,但它将毫无例外地发挥作用。

    public static TEnum ToEnum<TEnum>(this string strEnumValue, TEnum defaultValue)
    {
        if (!Enum.IsDefined(typeof(TEnum), strEnumValue))
            return defaultValue;
    
        return (TEnum)Enum.Parse(typeof(TEnum), strEnumValue);
    }
    

    值得注意的:A TryParse 方法已添加到.NET 4.0中。

        3
  •  19
  •   Simon Mourier    13 年前

    以下是的自定义实现 EnumTryParse . 与其他常见实现不同,它还支持用 Flags 属性。

        /// <summary>
        /// Converts the string representation of an enum to its Enum equivalent value. A return value indicates whether the operation succeeded.
        /// This method does not rely on Enum.Parse and therefore will never raise any first or second chance exception.
        /// </summary>
        /// <param name="type">The enum target type. May not be null.</param>
        /// <param name="input">The input text. May be null.</param>
        /// <param name="value">When this method returns, contains Enum equivalent value to the enum contained in input, if the conversion succeeded.</param>
        /// <returns>
        /// true if s was converted successfully; otherwise, false.
        /// </returns>
        public static bool EnumTryParse(Type type, string input, out object value)
        {
            if (type == null)
                throw new ArgumentNullException("type");
    
            if (!type.IsEnum)
                throw new ArgumentException(null, "type");
    
            if (input == null)
            {
                value = Activator.CreateInstance(type);
                return false;
            }
    
            input = input.Trim();
            if (input.Length == 0)
            {
                value = Activator.CreateInstance(type);
                return false;
            }
    
            string[] names = Enum.GetNames(type);
            if (names.Length == 0)
            {
                value = Activator.CreateInstance(type);
                return false;
            }
    
            Type underlyingType = Enum.GetUnderlyingType(type);
            Array values = Enum.GetValues(type);
            // some enums like System.CodeDom.MemberAttributes *are* flags but are not declared with Flags...
            if ((!type.IsDefined(typeof(FlagsAttribute), true)) && (input.IndexOfAny(_enumSeperators) < 0))
                return EnumToObject(type, underlyingType, names, values, input, out value);
    
            // multi value enum
            string[] tokens = input.Split(_enumSeperators, StringSplitOptions.RemoveEmptyEntries);
            if (tokens.Length == 0)
            {
                value = Activator.CreateInstance(type);
                return false;
            }
    
            ulong ul = 0;
            foreach (string tok in tokens)
            {
                string token = tok.Trim(); // NOTE: we don't consider empty tokens as errors
                if (token.Length == 0)
                    continue;
    
                object tokenValue;
                if (!EnumToObject(type, underlyingType, names, values, token, out tokenValue))
                {
                    value = Activator.CreateInstance(type);
                    return false;
                }
    
                ulong tokenUl;
                switch (Convert.GetTypeCode(tokenValue))
                {
                    case TypeCode.Int16:
                    case TypeCode.Int32:
                    case TypeCode.Int64:
                    case TypeCode.SByte:
                        tokenUl = (ulong)Convert.ToInt64(tokenValue, CultureInfo.InvariantCulture);
                        break;
    
                    //case TypeCode.Byte:
                    //case TypeCode.UInt16:
                    //case TypeCode.UInt32:
                    //case TypeCode.UInt64:
                    default:
                        tokenUl = Convert.ToUInt64(tokenValue, CultureInfo.InvariantCulture);
                        break;
                }
    
                ul |= tokenUl;
            }
            value = Enum.ToObject(type, ul);
            return true;
        }
    
        private static char[] _enumSeperators = new char[] { ',', ';', '+', '|', ' ' };
    
        private static object EnumToObject(Type underlyingType, string input)
        {
            if (underlyingType == typeof(int))
            {
                int s;
                if (int.TryParse(input, out s))
                    return s;
            }
    
            if (underlyingType == typeof(uint))
            {
                uint s;
                if (uint.TryParse(input, out s))
                    return s;
            }
    
            if (underlyingType == typeof(ulong))
            {
                ulong s;
                if (ulong.TryParse(input, out s))
                    return s;
            }
    
            if (underlyingType == typeof(long))
            {
                long s;
                if (long.TryParse(input, out s))
                    return s;
            }
    
            if (underlyingType == typeof(short))
            {
                short s;
                if (short.TryParse(input, out s))
                    return s;
            }
    
            if (underlyingType == typeof(ushort))
            {
                ushort s;
                if (ushort.TryParse(input, out s))
                    return s;
            }
    
            if (underlyingType == typeof(byte))
            {
                byte s;
                if (byte.TryParse(input, out s))
                    return s;
            }
    
            if (underlyingType == typeof(sbyte))
            {
                sbyte s;
                if (sbyte.TryParse(input, out s))
                    return s;
            }
    
            return null;
        }
    
        private static bool EnumToObject(Type type, Type underlyingType, string[] names, Array values, string input, out object value)
        {
            for (int i = 0; i < names.Length; i++)
            {
                if (string.Compare(names[i], input, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    value = values.GetValue(i);
                    return true;
                }
            }
    
            if ((char.IsDigit(input[0]) || (input[0] == '-')) || (input[0] == '+'))
            {
                object obj = EnumToObject(underlyingType, input);
                if (obj == null)
                {
                    value = Activator.CreateInstance(type);
                    return false;
                }
                value = obj;
                return true;
            }
    
            value = Activator.CreateInstance(type);
            return false;
        }
    
        4
  •  16
  •   Richard    14 年前

    最后你必须围绕 Enum.GetNames :

    public bool TryParseEnum<T>(string str, bool caseSensitive, out T value) where T : struct {
        // Can't make this a type constraint...
        if (!typeof(T).IsEnum) {
            throw new ArgumentException("Type parameter must be an enum");
        }
        var names = Enum.GetNames(typeof(T));
        value = (Enum.GetValues(typeof(T)) as T[])[0];  // For want of a better default
        foreach (var name in names) {
            if (String.Equals(name, str, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase)) {
                value = (T)Enum.Parse(typeof(T), name);
                return true;
            }
        }
        return false;
    }
    

    附加说明:

    • Enum.TryParse 包含在.NET 4中。看到这里 http://msdn.microsoft.com/library/dd991876(VS.100).aspx
    • 另一种方法是直接包装 Enum.Parse 捕获在失败时引发的异常。如果找到匹配项,这可能会更快,但如果没有,可能会变慢。根据您正在处理的数据,这可能是或不是一个净改进。

    编辑:刚刚看到一个更好的实现,它缓存了必要的信息: http://damieng.com/blog/2010/10/17/enums-better-syntax-improved-performance-and-tryparse-in-net-3-5

        5
  •  8
  •   Hugo Hilário    10 年前

    基于.NET 4.5

    下面的示例代码

    using System;
    
    enum Importance
    {
        None,
        Low,
        Medium,
        Critical
    }
    
    class Program
    {
        static void Main()
        {
        // The input value.
        string value = "Medium";
    
        // An unitialized variable.
        Importance importance;
    
        // Call Enum.TryParse method.
        if (Enum.TryParse(value, out importance))
        {
            // We now have an enum type.
            Console.WriteLine(importance == Importance.Medium);
        }
        }
    }
    

    参考文献: http://www.dotnetperls.com/enum-parse

        6
  •  4
  •   Jon Skeet    15 年前

    我有一个优化的实现,您可以在 UnconstrainedMelody . 实际上,它只是缓存名称列表,但它是以一种良好的、强类型的、一般约束的方式来实现的:)

        7
  •  3
  •   nevyn jfg956    6 年前
    enum EnumStatus
    {
        NAO_INFORMADO = 0,
        ENCONTRADO = 1,
        BLOQUEADA_PELO_ENTREGADOR = 2,
        DISPOSITIVO_DESABILITADO = 3,
        ERRO_INTERNO = 4,
        AGARDANDO = 5
    }
    

    if (Enum.TryParse<EnumStatus>(item.status, out status)) {
    
    }
    
        8
  •  2
  •   Ahmad Mageed    15 年前

    当前没有现成的enum.typarse。连接时已请求( Still no Enum.TryParse )并得到一个响应,指示可能包含在.NET 3.5之后的下一个框架中。您现在必须实施建议的解决方法。

        9
  •  1
  •   Philippe Leybaert    15 年前

    避免异常处理的唯一方法是使用getNames()方法,我们都知道对于公共应用程序逻辑,不应该滥用异常:)

        10
  •  1
  •   Nader Shirazie    15 年前

    是否允许缓存动态生成的函数/字典?

    因为您似乎事先不知道枚举的类型,所以第一次执行可能会生成一些后续执行可以利用的东西。

    甚至可以缓存Enum.GetNames()的结果。

    您是否正在尝试针对CPU或内存进行优化?你…吗 真的? 需要?

        11
  •  0
  •   Enum    15 年前

    正如其他人已经说过的,如果不使用Try&Catch,则需要使用IsDefined或GetNames… 这里有一些示例……它们基本上都是相同的,第一个处理可以为空的枚举。我更喜欢第二个,因为它是字符串的扩展,而不是枚举……但您可以根据需要混合它们!

    • www.objectreeference.net/post/enum-typarse-extension-method.aspx
    • flatlinerdoa.spaces.live.com/blog/cns!17124d03a9a052b0!605.条目
    • mironabramson.com/blog/post/2008/03/another-version-for-the-missing-method-enumtryparse.aspx
    • lazyloading.blogspot.com/2008/04/enumtryparse-with-net-35-extension.html
        12
  •  0
  •   SztupY    12 年前

    没有台盼,因为枚举的类型在运行时之前未知。如果一个typarse遵循与上述date.typarse方法相同的方法,则会在byref参数上引发一个隐式转换错误。

    我建议这样做:

    //1 line call to get value
    MyEnums enumValue = (Sections)EnumValue(typeof(Sections), myEnumTextValue, MyEnums.SomeEnumDefault);
    
    //Put this somewhere where you can reuse
    public static object EnumValue(System.Type enumType, string value, object NotDefinedReplacement)
    {
        if (Enum.IsDefined(enumType, value)) {
            return Enum.Parse(enumType, value);
        } else {
            return Enum.Parse(enumType, NotDefinedReplacement);
        }
    }
    
        13
  •  -1
  •   Spence    15 年前

    查看枚举类(结构?)本身。这上面有一个解析方法,但我不确定是什么样的。

        14
  •  -2
  •   Naveed Ahmed    14 年前

    此方法将转换枚举类型:

      public static TEnum ToEnum<TEnum>(object EnumValue, TEnum defaultValue)
        {
            if (!Enum.IsDefined(typeof(TEnum), EnumValue))
            {
                Type enumType = Enum.GetUnderlyingType(typeof(TEnum));
                if ( EnumValue.GetType() == enumType )
                {
                    string name = Enum.GetName(typeof(HLink.ViewModels.ClaimHeaderViewModel.ClaimStatus), EnumValue);
                    if( name != null)
                        return (TEnum)Enum.Parse(typeof(TEnum), name);
                    return defaultValue;
                }
            }
            return (TEnum)Enum.Parse(typeof(TEnum), EnumValue.ToString());
        } 
    

    它检查底层类型,并根据它获取要分析的名称。如果一切都失败,它将返回默认值。