如何使用泛型TryParse和枚举?

43

我正在尝试编写一个通用函数,该函数从用户字符串中获取值并尝试将其解析为枚举值,如下所示:

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值而不是使其成为真正的通用方法吗? - Jon Skeet
7个回答

72

TryParse 方法 具有以下签名:

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

它有一个通用类型参数TEnum,必须是一个结构体,用于确定正在解析的枚举类型。当您未明确提供它时(如您所做的那样),它将采用您提供为result参数的任何内容的类型,而在您的情况下,该类型为Enum(而不是枚举本身的类型)。

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

您可以通过删除Type参数并给方法添加一个具有相同约束条件(即struct)的通用类型参数来解决这个问题。TryParse函数上的通用类型参数命名为TEnum

因此,请尝试使用以下内容(我已将通用类型参数命名为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是一种通用的方法... http://msdn.microsoft.com/zh-cn/library/system.enum.aspx - eyossi
没错。我从未说过不是这样。它的签名是TryParse<TEnum>(string value, bool ignoreCase, out TEnum result) where TEnum : struct, new(),如果你指定了result,它将使用该变量的类型来确定TEnum,省去了显式指定的步骤。 - Daniel A.A. Pelsmaeker
3
我也遇到了同样的错误:要使用泛型类型或方法 'System.Enum.TryParse<TEnum>(string, bool, out TEnum)' 的参数 'TEnum',必须将类型 'TEnum' 设为非空值类型。 - Rami
我在进行此操作时出现的错误是“'new()' 约束无法与 'struct' 约束一起使用”。 - PandaWood
1
@PandaWood 我相信 new() 以前是多余的,但现在似乎被禁止了。我已经更新了答案。 - Daniel A.A. Pelsmaeker
显示剩余2条评论

8

很久以前,在Visual Studio 2005时代,我为枚举类型自己编写了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方法的方法。希望这可以帮助到您。

7
你应该编写一个通用方法:
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)”中的参数“TEnum”。 - Rami
请遵循更新的代码(我添加了 where T : struct, new())并查看使用示例。 - eyossi
你是对的...我现在已经修复了它,而且它可以工作了。 对于枚举约束,我使用了这个 - https://dev59.com/53VD5IYBdhLWcg3wHn2d - eyossi

3

从C# 7开始,我们可以将一个通用类型指定为枚举

where TCustomValidatorsEnum : Enum

如评论所述

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

按照OP提供的方式编译会出现以下错误:

在泛型类型或方法“System.Enum.TryParse(string, bool, out TEnum)”中,参数“TEnum”必须是非可空值类型。

但是,使用这个版本则可以编译通过:

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

1
正确的方式(就像微软自己所做的那样)是where TCustomValidatorsEnum : struct, Enum。这样警告就会消失,同时你仍然可以正确地约束类型。 - JHBonarius

1

Enum.TryParse 是一种通用方法,这意味着其泛型类型参数必须在编译时已知。这反过来又意味着,是的,您必须将 resultInputType 声明为一个具体的枚举类型,以便代码能够编译。

如果您仔细思考一下,原始代码有点过于乐观了:它没有说明应检查哪个枚举类型是否具有与 userInputString 相等的成员。没有这些信息,TryParse 怎么能工作呢?


1
这很糟糕。通用类型约束使得实现一个检查通用类型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接受“type”实例,但Enum.TryParse没有这样的重载,只有那个过度约束的通用实现。 - Triynko

0

字符串扩展方法

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

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

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

0
    /// <summary>
    ///     Usage:
    ///         yourEnum = yourString.ToEnum<TEnum>()
    /// </summary>
    /// 
    /// <Parameters>
    ///     <typeparam name="TEnum"></typeparam>
    ///     <param name="value"></param>
    /// </Parameters>
    /// 
    /// <returns>
    ///     (TEnum) parsed
    /// </returns>
    /// 
    public static TEnum ToEnum<TEnum>(this string value)
    {

        var parsed = Enum.Parse(typeof(TEnum), value, true);

        return (TEnum)parsed;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接