我正在构建一个函数来扩展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以来,这个问题已经得到解决和支持,我已经更改了公认的答案,尽管出于学术和历史兴趣,充分阅读顶部的答案是值得的:)


当前回答

我将Vivek的解决方案封装到一个可以重用的实用程序类中。请注意,你仍然应该在你的类型上定义类型约束“where T: struct, IConvertible”。

using System;

internal static class EnumEnforcer
{
    /// <summary>
    /// Makes sure that generic input parameter is of an enumerated type.
    /// </summary>
    /// <typeparam name="T">Type that should be checked.</typeparam>
    /// <param name="typeParameterName">Name of the type parameter.</param>
    /// <param name="methodName">Name of the method which accepted the parameter.</param>
    public static void EnforceIsEnum<T>(string typeParameterName, string methodName)
        where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum)
        {
            string message = string.Format(
                "Generic parameter {0} in {1} method forces an enumerated type. Make sure your type parameter {0} is an enum.",
                typeParameterName,
                methodName);

            throw new ArgumentException(message);
        }
    }

    /// <summary>
    /// Makes sure that generic input parameter is of an enumerated type.
    /// </summary>
    /// <typeparam name="T">Type that should be checked.</typeparam>
    /// <param name="typeParameterName">Name of the type parameter.</param>
    /// <param name="methodName">Name of the method which accepted the parameter.</param>
    /// <param name="inputParameterName">Name of the input parameter of this page.</param>
    public static void EnforceIsEnum<T>(string typeParameterName, string methodName, string inputParameterName)
        where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum)
        {
            string message = string.Format(
                "Generic parameter {0} in {1} method forces an enumerated type. Make sure your input parameter {2} is of correct type.",
                typeParameterName,
                methodName,
                inputParameterName);

            throw new ArgumentException(message);
        }
    }

    /// <summary>
    /// Makes sure that generic input parameter is of an enumerated type.
    /// </summary>
    /// <typeparam name="T">Type that should be checked.</typeparam>
    /// <param name="exceptionMessage">Message to show in case T is not an enum.</param>
    public static void EnforceIsEnum<T>(string exceptionMessage)
        where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum)
        {
            throw new ArgumentException(exceptionMessage);
        }
    }
}

其他回答

希望这对你有帮助:

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

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

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

C# ≥ 7.3

从c# 7.3 (Visual Studio 2017≥v15.7可用)开始,此代码现在完全有效:

public static TEnum Parse<TEnum>(string value)
    where TEnum : struct, Enum
{
 ...
}

C# ≤ 7.2

通过滥用约束继承,可以让真正的编译器强制枚举约束。下面的代码同时指定了类和结构约束:

public abstract class EnumClassUtils<TClass>
where TClass : class
{

    public static TEnum Parse<TEnum>(string value)
    where TEnum : struct, TClass
    {
        return (TEnum) Enum.Parse(typeof(TEnum), value);
    }

}

public class EnumUtils : EnumClassUtils<Enum>
{
}

用法:

EnumUtils.Parse<SomeEnum>("value");

注意:这是在c# 5.0语言规范中特别说明的:

如果类型参数S依赖于类型参数T,则: […它对…有效 S具有值类型约束,T具有引用类型 约束。这有效地将T限制为System类型。对象, 系统。ValueType、系统。Enum和任何接口类型。

还应该考虑到,由于使用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或更高版本,语法是完美的。

您可以为该类定义一个静态构造函数,该构造函数将检查类型T是否是enum,如果不是则抛出异常。这是Jeffery Richter在他的书《CLR via c#》中提到的方法。

internal sealed class GenericTypeThatRequiresAnEnum<T> {
    static GenericTypeThatRequiresAnEnum() {
        if (!typeof(T).IsEnum) {
        throw new ArgumentException("T must be an enumerated type");
        }
    }
}

然后在解析方法中,你可以使用enumel . parse (typeof(T), input, true)将字符串转换为enum。最后一个true参数用于忽略输入的大小写。