我正在构建一个函数来扩展Enum。解析概念

允许在没有找到Enum值的情况下解析默认值 不区分大小写

所以我写下了以下内容:

public static T GetEnumFromString<T>(string value, T defaultValue) where T : Enum
{
    if (string.IsNullOrEmpty(value)) return defaultValue;
    foreach (T item in Enum.GetValues(typeof(T)))
    {
        if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
    }
    return defaultValue;
}

我得到一个错误约束不能是特殊类System.Enum。

很好,但是是否有一种变通方法来允许通用Enum,或者我将不得不模仿Parse函数并将类型作为属性传递,这将强制对代码进行丑陋装箱要求。

以下所有建议都非常感谢,谢谢。

已经确定(我已经离开了循环以保持大小写不敏感-我在解析XML时使用这个)

public static class EnumUtils
{
    public static T ParseEnum<T>(string value, T defaultValue) where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum) throw new ArgumentException("T must be an enumerated type");
        if (string.IsNullOrEmpty(value)) return defaultValue;

        foreach (T item in Enum.GetValues(typeof(T)))
        {
            if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
        }
        return defaultValue;
    }
}

编辑:(2015年2月16日)Christopher currents在下面的MSIL或f#中发布了一个编译器强制类型安全的通用解决方案,非常值得一看,并获得好评。如果解决方案在页面上方出现气泡,我将删除此编辑。

编辑2:(2021年4月13日)自从c# 7.3以来,这个问题已经得到解决和支持,我已经更改了公认的答案,尽管出于学术和历史兴趣,充分阅读顶部的答案是值得的:)


当前回答

还应该考虑到,由于使用Enum约束的c# 7.3版本是开箱即用的,无需做额外的检查和其他工作。

因此,如果你已经将项目的语言版本更改为c# 7.3,下面的代码将会完美地工作:

    private static T GetEnumFromString<T>(string value, T defaultValue) where T : Enum
    {
        // Your code goes here...
    }

如果你不知道如何将语言版本更改为c# 7.3,请参阅下面的截图:

编辑1 -需要的Visual Studio版本,并考虑ReSharper

要让Visual Studio能够识别新的语法,您至少需要版本15.7。你可以在微软的发布说明中找到,参见Visual Studio 2017 15.7发布说明。谢谢@ mohammedelshawaf指出了这个有效的问题。

请注意,在我写这篇文章时,ReSharper 2018.1还不支持c# 7.3。ReSharper激活它突出显示Enum约束作为一个错误告诉我不能使用“系统”。数组”、“系统。委托”、“系统。枚举”、“系统。ValueType', 'object'作为类型参数约束。 ReSharper建议作为一个快速修复删除'Enum'约束类型参数T的方法

然而,如果你在Tools -> Options -> ReSharper Ultimate -> General下暂时关闭ReSharper,你会发现如果你使用VS 15.7或更高版本和c# 7.3或更高版本,语法是完美的。

其他回答

注意系统。Enum Parse()和TryParse()方法仍然有where struct约束,而不是where Enum,因此不会编译:

    bool IsValid<TE>(string attempted) where TE : Enum
    {
        return Enum.TryParse(attempted, out TE _);
    }

但这将:

bool Ok<TE>(string attempted) where TE : struct,Enum
{
    return Enum.TryParse(attempted, out var _)
}

因此,where struct,Enum可能比where Enum更可取

希望这对你有帮助:

public static TValue ParseEnum<TValue>(string value, TValue defaultValue)
                  where TValue : struct // enum 
{
      try
      {
            if (String.IsNullOrEmpty(value))
                  return defaultValue;
            return (TValue)Enum.Parse(typeof (TValue), value);
      }
      catch(Exception ex)
      {
            return defaultValue;
      }
}

Edit

这个问题现在已经被朱利安·勒博斯昆完美地回答了。 我还想用ignoreCase, defaultValue和可选参数扩展他的答案,同时添加TryParse和ParseOrDefault。

public abstract class ConstrainedEnumParser<TClass> where TClass : class
// value type constraint S ("TEnum") depends on reference type T ("TClass") [and on struct]
{
    // internal constructor, to prevent this class from being inherited outside this code
    internal ConstrainedEnumParser() {}
    // Parse using pragmatic/adhoc hard cast:
    //  - struct + class = enum
    //  - 'guaranteed' call from derived <System.Enum>-constrained type EnumUtils
    public static TEnum Parse<TEnum>(string value, bool ignoreCase = false) where TEnum : struct, TClass
    {
        return (TEnum)Enum.Parse(typeof(TEnum), value, ignoreCase);
    }
    public static bool TryParse<TEnum>(string value, out TEnum result, bool ignoreCase = false, TEnum defaultValue = default(TEnum)) where TEnum : struct, TClass // value type constraint S depending on T
    {
        var didParse = Enum.TryParse(value, ignoreCase, out result);
        if (didParse == false)
        {
            result = defaultValue;
        }
        return didParse;
    }
    public static TEnum ParseOrDefault<TEnum>(string value, bool ignoreCase = false, TEnum defaultValue = default(TEnum)) where TEnum : struct, TClass // value type constraint S depending on T
    {
        if (string.IsNullOrEmpty(value)) { return defaultValue; }
        TEnum result;
        if (Enum.TryParse(value, ignoreCase, out result)) { return result; }
        return defaultValue;
    }
}

public class EnumUtils: ConstrainedEnumParser<System.Enum>
// reference type constraint to any <System.Enum>
{
    // call to parse will then contain constraint to specific <System.Enum>-class
}

用法示例:

WeekDay parsedDayOrArgumentException = EnumUtils.Parse<WeekDay>("monday", ignoreCase:true);
WeekDay parsedDayOrDefault;
bool didParse = EnumUtils.TryParse<WeekDay>("clubs", out parsedDayOrDefault, ignoreCase:true);
parsedDayOrDefault = EnumUtils.ParseOrDefault<WeekDay>("friday", ignoreCase:true, defaultValue:WeekDay.Sunday);

Old

通过使用评论和“新”发展,我对Vivek的答案进行了旧的改进:

为了方便用户使用TEnum 为额外的约束检查添加更多的接口约束 让TryParse用现有参数处理ignoreCase (在VS2010/中引入。4)净 可选地使用通用缺省值(在VS2005/中引入)。2)净 使用可选参数(在VS2010/中引入)。Net 4)默认值,用于defaultValue和ignoreCase

导致:

public static class EnumUtils
{
    public static TEnum ParseEnum<TEnum>(this string value,
                                         bool ignoreCase = true,
                                         TEnum defaultValue = default(TEnum))
        where TEnum : struct,  IComparable, IFormattable, IConvertible
    {
        if ( ! typeof(TEnum).IsEnum) { throw new ArgumentException("TEnum must be an enumerated type"); }
        if (string.IsNullOrEmpty(value)) { return defaultValue; }
        TEnum lResult;
        if (Enum.TryParse(value, ignoreCase, out lResult)) { return lResult; }
        return defaultValue;
    }
}

我用二元酸对样品进行了修改。这个版本只适用于枚举,不允许结构体通过。

public static T ParseEnum<T>(string enumString)
    where T : struct // enum 
    {
    if (String.IsNullOrEmpty(enumString) || !typeof(T).IsEnum)
       throw new Exception("Type given must be an Enum");
    try
    {

       return (T)Enum.Parse(typeof(T), enumString, true);
    }
    catch (Exception ex)
    {
       return default(T);
    }
}

这是我的看法。结合答案和MSDN

public static TEnum ParseToEnum<TEnum>(this string text) where TEnum : struct, IConvertible, IComparable, IFormattable
{
    if (string.IsNullOrEmpty(text) || !typeof(TEnum).IsEnum)
        throw new ArgumentException("TEnum must be an Enum type");

    try
    {
        var enumValue = (TEnum)Enum.Parse(typeof(TEnum), text.Trim(), true);
        return enumValue;
    }
    catch (Exception)
    {
        throw new ArgumentException(string.Format("{0} is not a member of the {1} enumeration.", text, typeof(TEnum).Name));
    }
}

MSDN源