如何将通用 Tryparse 与 Enum 一起使用?

Posted

技术标签:

【中文标题】如何将通用 Tryparse 与 Enum 一起使用?【英文标题】:How to use generic Tryparse with Enum? 【发布时间】:2012-05-21 13:04:17 【问题描述】:

我正在尝试构建从用户字符串获取的通用函数,并尝试将其解析为 Enum 值,如下所示:

private Enum getEnumStringEnumType(Type i_EnumType)
    
        string userInputString = string.Empty;
        Enum resultInputType;
        bool enumParseResult = false;

        while (!enumParseResult)
                        
            userInputString = System.Console.ReadLine();
            enumParseResult = Enum.TryParse(userInputString, true, out resultInputType);
        
    

但我明白了:

The type 'System.Enum' must be a non-nullable value type in order to use it as parameter 'TEnum' in the generic type or method 'System.Enum.TryParse<TEnum>(string, bool, out TEnum)    .

错误意味着我需要为 resultInputType 声明一个特定的枚举? 我怎样才能解决这个问题 ? 谢谢。

【问题讨论】:

当你说“泛型函数”时——你的方法不是泛型的。您是否需要能够将类型指定为 Type 值而不是使其成为真正的泛型方法? 【参考方案1】:

TryParse method 具有以下签名:

TryParse<TEnum>(string value, bool ignoreCase, out TEnum result)
    where TEnum : struct

它有一个泛型类型参数TEnum,它必须是一个struct,用于确定被解析的枚举类型。当您没有明确提供它时(就像您所做的那样),它将采用您提供的任何类型作为 result 参数,在您的情况下是 Enum 类型(而不是枚举本身的类型) .

注意Enum is a class(尽管它继承自ValueType),因此它不满足TEnum是一个结构的要求。

您可以通过删除Type 参数并为该方法提供一个泛型类型参数来解决此问题,该参数具有与TryParse 函数上的泛型类型参数相同的约束(即struct)。

所以试试这个,我将泛型类型参数命名为TEnum

private static TEnum GetEnumStringEnumType<TEnum>()
    where TEnum : struct

    string userInputString = string.Empty;
    TEnum resultInputType = default(TEnum);
    bool enumParseResult = false;

    while (!enumParseResult)
                    
        userInputString = System.Console.ReadLine();
        enumParseResult = Enum.TryParse(userInputString, true, out resultInputType);
    
    return resultInputType;

要调用该方法,请使用:

GetEnumStringEnumType<MyEnum>();

【讨论】:

Enum.TryParse 是通用的...msdn.microsoft.com/en-us/library/system.enum.aspx 没错。我从来没有说过不是。它的签名是TryParse&lt;TEnum&gt;(string value, bool ignoreCase, out TEnum result) where TEnum : struct, new(),如果您指定result,它将使用该变量的类型来确定TEnum,从而避免您显式指定它。 我得到同样的错误:类型 'TEnum' 必须是不可为空的值类型才能将其用作泛型类型或方法 'System.Enum.TryParse(string, bool, out TEnum). 我这样做时的错误是“'new()' 约束不能与 'struct' 约束一起使用” @PandaWood 我相信new() 曾经是多余的,但现在似乎被禁止了。我更新了答案。【参考方案2】:

你应该做一个通用方法:

private T getEnumStringEnumType<T>() where T : struct, IConvertible
    
        string userInputString = string.Empty;
        T resultInputType = default(T);
        bool enumParseResult = false;

        while (!enumParseResult)
        
            userInputString = System.Console.ReadLine();
            enumParseResult = Enum.TryParse<T>(userInputString, out resultInputType);
        

        return resultInputType;
    

用法:

public enum myEnum  val1, val2 

myEnum enumValue = getEnumStringEnumType<myEnum>();

【讨论】:

我得到了同样的错误:类型“T”必须是不可为空的值类型,才能将其用作泛型类型或方法“System.Enum.TryParse”中的参数“TEnum”(string, out TEnum); 遵循更新的代码(我添加了 where T : struct, new())。并查看使用示例 你说得对……我现在修好了它,它可以工作了。对于我使用的枚举约束 - ***.com/questions/79126/…【参考方案3】:

很久以前,在 Visual Studio 2005 时代,我为 Enum 上的 TryParse 创建了自己的方法。我最近才发现 2008 年的实现,我对它的限制性不满意,特别是考虑到它是一个 TRY PARSE 方法;意味着程序员正在测试输入!

一般来说,我更喜欢使用信任程序员知道他在做什么的方法:)

我的实现如下:

public static bool EnumTryParse<T>(string input, out T theEnum)

    foreach (string en in Enum.GetNames(typeof(T)))
    
        if (en.Equals(input, StringComparison.CurrentCultureIgnoreCase))
        
            theEnum = (T)Enum.Parse(typeof(T), input, true);
            return true;
        
    

    theEnum = default(T);
    return false;

缺少 where T:struct 可以让开发人员信任它,但它允许您使用未知的通用枚举进行编译。

如果您想在转换为指定的枚举时进行整数比较,则可以在 Enum.GetValues 上创建一个循环方法。

希望这会有所帮助。

【讨论】:

【参考方案4】:

Enum.TryParse 是一个泛型方法,这意味着它的泛型类型参数必须在编译时知道。这反过来意味着是的,您必须将resultInputType 声明为特定枚举类型才能编译代码。

如果你仔细想想,原来的代码有点过于乐观:它没有说哪个枚举类型应该检查名称等于userInputString的成员。如果没有这些信息,TryParse 怎么能工作?

【讨论】:

这太糟糕了。泛型类型约束使得无法实现检查泛型类型 T 是否为枚举类型并对其进行解析的方法。例如:if (typeof(T).IsEnum) if (Enum.TryParse(s, true, out T outputValue)) return outputValue; else throw new Exception("Parse failed."); else return Convert.ChangeType(s, typeof(T)); 该代码将无法编译,因为 TryParse 方法要求 T 是一个结构。此外,虽然 Enum.Parse 接受“类型”实例,但 Enum.TryParse 没有这样的重载,只有过度约束的泛型实现。【参考方案5】:

从 C# 7 开始,我们可以将泛型类型指定为 Enum

where TCustomValidatorsEnum : Enum

如评论

TryParse<TEnum>(string value, bool ignoreCase, out TEnum result)
    where TEnum : struct

将按照 OP 中的说明进行编译投诉:

类型“System.Enum”必须是不可为空的值类型,以便 将其用作泛型类型或方法中的参数“TEnum” 'System.Enum.TryParse(string, bool, out TEnum)

这个版本确实可以编译:

var converted = Enum.TryParse(typeof(TCustomValidatorsEnum), schemaConstraintType, true, out var customValidationEnum);

【讨论】:

【参考方案6】:

字符串扩展方法

public TEnum ToEnum<TEnum>(this string value, TEnum defaultValue)

if (string.IsNullOrEmpty(value))return defaultValue;

return Enum.Parse(typeof(TEnum), value, true);

【讨论】:

以上是关于如何将通用 Tryparse 与 Enum 一起使用?的主要内容,如果未能解决你的问题,请参考以下文章

带有标志属性的 Enum.TryParse

为什么Enum.TryParse需要约束,其中T:struct

如何将 int.TryParse 与可为空的 int 一起使用? [复制]

将 int.TryParse 与可为空的 int 一起使用 [重复]

通用 TryParse

如果 Int32.TryParse 无法解析字符串,如何将其输出设置为 null? [复制]