如何尝试解析枚举值?

发布于 2024-07-26 18:00:54 字数 505 浏览 3 评论 0原文

我想编写一个函数,它可以根据 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;
}

I want to write a function which can validate a given value (passed as a string) against possible values of an enum. In the case of a match, it should return the enum instance; otherwise, it should return a default value.

The function may not internally use try/catch, which excludes using Enum.Parse, which throws an exception when given an invalid argument.

I'd like to use something along the lines of a TryParse function to implement this:

public static TEnum ToEnum<TEnum>(this string strEnumValue, TEnum defaultValue)
{
   object enumValue;
   if (!TryParse (typeof (TEnum), strEnumValue, out enumValue))
   {
       return defaultValue;
   }
   return (TEnum) enumValue;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

×眷恋的温暖 2024-08-02 18:00:54

Enum.IsDefined 将完成任务。 它可能不如 TryParse 那样高效,但它无需异常处理即可工作。

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);
}

值得注意的是:添加了 TryParse 方法在.NET 4.0 中。

Enum.IsDefined will get things done. It may not be as efficient as a TryParse would probably be, but it will work without exception handling.

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);
}

Worth noting: a TryParse method was added in .NET 4.0.

冷月断魂刀 2024-08-02 18:00:54

正如其他人所说,您必须实现自己的 TryParse。 Simon Mourier 提供了一个完整的实施方案,可以处理所有事情。

如果您使用位域枚举(即标志),您还必须处理像 "MyEnum.Val1|MyEnum.Val2" 这样的字符串,它是两个枚举值的组合。 如果您仅使用此字符串调用 Enum.IsDefined,它将返回 false,即使 Enum.Parse 正确处理它。

更新

正如 Lisa 和 Christian 在评论中提到的,Enum.TryParse 现在可用于 .NET4 及更高版本中的 C#。

MSDN 文档

As others have said, you have to implement your own TryParse. Simon Mourier is providing a full implementation which takes care of everything.

If you are using bitfield enums (i.e. flags), you also have to handle a string like "MyEnum.Val1|MyEnum.Val2" which is a combination of two enum values. If you just call Enum.IsDefined with this string, it will return false, even though Enum.Parse handles it correctly.

Update

As mentioned by Lisa and Christian in the comments, Enum.TryParse is now available for C# in .NET4 and up.

MSDN Docs

我ぃ本無心為│何有愛 2024-08-02 18:00:54

这是 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;
    }

Here is a custom implementation of EnumTryParse. Unlike other common implementations, it also supports enum marked with the Flags attribute.

    /// <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;
    }
奢望 2024-08-02 18:00:54

最后,您必须围绕 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

In the end you have to implement this around 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;
}

Additional notes:

  • Enum.TryParse is included in .NET 4. See here http://msdn.microsoft.com/library/dd991876(VS.100).aspx
  • Another approach would be to directly wrap Enum.Parse catching the exception thrown when it fails. This could be faster when a match is found, but will likely to slower if not. Depending on the data you are processing this may or may not be a net improvement.

EDIT: Just seen a better implementation on this, which caches the necessary information: http://damieng.com/blog/2010/10/17/enums-better-syntax-improved-performance-and-tryparse-in-net-3-5

累赘 2024-08-02 18:00:54

基于 .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

Based on .NET 4.5

Sample code below

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);
    }
    }
}

Reference : http://www.dotnetperls.com/enum-parse

怎言笑 2024-08-02 18:00:54
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)) {

}
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)) {

}
段念尘 2024-08-02 18:00:54

我有一个优化的实现,您可以在 UnconstrainedMelody 中使用。 实际上,它只是缓存名称列表,但它是以一种很好的、​​强类型的、通用约束的方式这样做的:)

I have an optimised implementation you could use in UnconstrainedMelody. Effectively it's just caching the list of names, but it's doing so in a nice, strongly typed, generically constrained way :)

萌能量女王 2024-08-02 18:00:54

目前还没有开箱即用的 Enum.TryParse。 已在 Connect 上请求此操作(仍然没有 Enum.TryParse )并得到了回应,表明可能包含在 .NET 3.5 之后的下一个框架中。 您现在必须实施建议的解决方法。

There's currently no out of the box Enum.TryParse. This has been requested on Connect (Still no Enum.TryParse) and got a response indicating possible inclusion in the next framework after .NET 3.5. You'll have to implement the suggested workarounds for now.

待"谢繁草 2024-08-02 18:00:54

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

The only way to avoid exception handling is to use the GetNames() method, and we all know that exceptions shouldn't be abused for common application logic :)

眼前雾蒙蒙 2024-08-02 18:00:54

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

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

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

您是否正在尝试优化 CPU 或内存? 您真的需要吗?

Is caching a dynamically generated function/dictionary permissable?

Because you don't (appear to) know the type of the enum ahead of time, the first execution could generate something subsequent executions could take advantage of.

You could even cache the result of Enum.GetNames()

Are you trying to optimize for CPU or Memory? Do you really need to?

趴在窗边数星星i 2024-08-02 18:00:54

看一下 Enum 类(struct ?)本身。 有一个 Parse 方法,但我不确定 tryparse。

Have a look at the Enum class (struct ? ) itself. There is a Parse method on that but I'm not sure about a tryparse.

情域 2024-08-02 18:00:54

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

  • www.objectreference.net/post/Enum-TryParse-Extension-Method.aspx
  • flatlinerdoa.spaces.live.com/blog/cns!17124D03A9A052B0!605.entry
  • mironabramson.com/blog/post/2008/03/Another-version- for-the-missing-method-EnumTryParse.aspxlazyloading.blogspot.com/2008/04/enumtryparse-with-net-35-extension.html

As others already said, if you don't use Try&Catch, you need to use IsDefined or GetNames...
Here are some samples...they basically are all the same, the first one handling nullable enums. I prefer the 2nd one as it's an extension on strings, not enums...but you can mix them as you want!

  • www.objectreference.net/post/Enum-TryParse-Extension-Method.aspx
  • flatlinerdoa.spaces.live.com/blog/cns!17124D03A9A052B0!605.entry
  • 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
平定天下 2024-08-02 18:00:54

没有 TryParse,因为 Enum 的类型直到运行时才知道。 遵循与 Date.TryParse 方法相同的方法的 TryParse 会在 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);
    }
}

There is not a TryParse because the Enum's type is not known until runtime. A TryParse that follows the same methodology as say the Date.TryParse method would throw an implicit conversion error on the ByRef parameter.

I suggest doing something like this:

//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);
    }
}
纵性 2024-08-02 18:00:54

此方法将转换枚举类型:

  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());
    } 

它检查基础类型并根据它获取名称进行解析。 如果一切失败,它将返回默认值。

This method will convert a type of enum:

  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());
    } 

It checks the underlying type and get the name against it to parse. If everything fails it will return default value.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文