如何测试枚举是否已定义,同时忽略大小写?

42
以下是一个通用的静态方法,它接受一个字符串并返回一个枚举类型(enum)。因为我将 ignoreCase 参数设置为 true,所以它可以很好地忽略大小写。
但是,我还想测试枚举值是否存在,但似乎没有一个 ignoreCase 参数的 enum.IsDefined 方法来实现这一点。
如何在测试枚举定义时忽略大小写呢?
using System;

namespace TestEnum2934234
{
    class Program
    {
        static void Main(string[] args)
        {
            LessonStatus lessonStatus = StringHelpers.ConvertStringToEnum<LessonStatus>("prepared");
            ReportStatus reportStatus = StringHelpers.ConvertStringToEnum<ReportStatus>("finished");

            Console.WriteLine(lessonStatus.ToString());
            Console.WriteLine(reportStatus.ToString());
            Console.ReadLine();
        }
    }

    public static class StringHelpers
    {
        public static T ConvertStringToEnum<T>(string text)
        {
            if (Enum.IsDefined(typeof(T), text)) //does not have ignoreCase parameter
                return (T)Enum.Parse(typeof(T), text, true);
            else
                return default(T);
        }
    }

    public enum LessonStatus
    {
        Defined,
        Prepared,
        Practiced,
        Recorded
    }

    public enum ReportStatus
    {
        Draft,
        Revising,
        Finished
    }
}

3
对于您的枚举类型,建议考虑将默认值设为 None。这不仅是良好的编程习惯,而且现在情况是这样的:如果您向 ConvertStringToEnum 传递字符串“Foo”,则会获得看似有效的枚举值。 - Marc
10个回答

44
public enum MyEnum
{
    Bar,
    Foo
}

class Program
{
    static void Main(string[] args)
    {
        var containsFoo = Enum.GetNames(typeof(MyEnum)).Any(x => x.ToLower() == "foo");
        Console.WriteLine(containsFoo);
    }
}

这是最好的解决方案! - Merna Mustafa

33

除了@Darin的答案外,在.NET 4.0中,Enum类型现在还有一个TryParse方法:

MyEnum result;
Enum.TryParse("bar", true, out result);

记住的重要一点是,Parse和TryParse在行为上有根本的区别。Parse方法会抛出异常,而TryParse方法则不会。如果你可能要解析许多项,这是非常重要的知识。


5
对于字符串化的数字值,这可能会返回意外的结果。例如,如果存在一个枚举值为1的枚举, Enum.TryParse("01", true, out result)将返回true 。另一方面,Darin的答案只匹配枚举名称。 - Ben Cottrell
1
@BenCottrell Enum.TryParse("01", true, out result)即使枚举中没有值为1的情况下也会返回true。需要注意这一点。为了更加健壮的解决方案,需要同时使用两种方法。 - nawfal

9
您也许可以简单地使用Enum.TryParse,如其他人所说。
然而,如果您想要一个更强大/通用的转换,使您能够转换更多类型而不仅仅是字符串,那么您还需要使用Enum.IsDefined。不幸的是,正如您发现的那样,Enum.IsDefined 区分大小写。 Enum.TryParse 是(可以)区分大小写的。但不幸的是,它允许超出范围的整数通过!
因此,解决方案是将它们结合在一起(顺序很重要)。
我编写了一个扩展方法,可以实现这一点。它允许从字符串、int/int? 和任何其他 Enum/Enum? 类型进行转换,如下所示:
string value1 = "Value1";
Enum2 enum2 = value1.ParseToEnum<Enum2>();
Debug.Assert(enum2.ToString() == value1);

Enum1 enum1 = Enum1.Value1;
enum2 = enum1.ParseToEnum<Enum2>();
Debug.Assert(enum2.ToString() == enum1.ToString());

int value2 = 1;
enum2 = value2.ParseToEnum<Enum2>();
Debug.Assert(enum2.GetHashCode() == value2);

这是方法的核心。这是回答你问题的转换部分。变量valueobject类型,因为我有不同类型作为主要输入的“重载”(见上文),但如果你只想要一个string类型的变量,你也可以做到这一点(显然将value.ToString()改为value)。
if (value != null)
{
    TEnum result;
    if (Enum.TryParse(value.ToString(), true, out result))
    {
        // since an out-of-range int can be cast to TEnum, double-check that result is valid
        if (Enum.IsDefined(typeof(TEnum), result.ToString()))
        {
            return result;
        }
    }
}

我的扩展方法还有更多功能……它允许您指定默认值,可以很好地处理超出范围的整数,并完全不区分大小写。如果有人感兴趣,我可以发布更多内容。


1
对我来说关键是使用 TryParse 的输出结果 .ToString 的想法;谢谢。其他人只建议 TryParse,但会错过超出范围的整数,这可能导致错误的匹配。 - Syntax
你在示例代码中的 Enum.IsDefined() 调用是完全冗余的,没有任何价值。 - gknicker
那行代码上面的注释难道没有解释它的必要性吗?我加进去是因为我测试过并发现超出范围的整数可以被解析,即使枚举没有与该整数对应的值。你有测试过这段代码吗? - sliderhouserules

5

请使用Enum.TryParse替代:

T val;

if(Enum.TryParse(text, true, out val))
    return val;
else 
    return default(T);

你的 else 子句是多余的。代码写成 Enum.TryParse(text, true, out T val); return val; 会得到完全相同的结果。 - gknicker

5

我正在使用Compact Framework 3.5,并且:

Enum.TryParse

...不存在。但它有:

Enum.IsDefined

但是它不支持ignoreCase参数。我想要兼顾两者,所以想出了这个(作为一个辅助方法)...

public bool TryParse<TEnum>(string value, bool ignoreCase, ref TEnum result) where TEnum : struct
{
    bool parsed;
    try
    {
        result = (TEnum)Enum.Parse(typeof(TEnum), value, ignoreCase);
        parsed = true;
    }
    catch { }
    return parsed;
}

HTH


1
如果你传递一个在枚举中不存在的整数值字符串,这种方法就会失败。尝试将“55”解析为 System.DayOfWeek 枚举。 - Mass Dot Net

1

我有一个类似的问题,使用了.Enum.TryPase(设置忽略大小写标志为true)和Enum.IsDefined的组合。请考虑以下内容作为您助手类的简化:

public static class StringHelpers
{
    public static T ConvertStringToEnum<T>(string text)
    {
        T result;
        return Enum.TryParse(text, true, out result)
            && Enum.IsDefined(result.ToString())
                ? result
                : default(T);
    }
}

顺便提一下,由于帮助类是静态的并且方法也是静态的 - 我们可以将其作为string的扩展方法。

public static class StringExtensions
{
    public static TEnum ToEnum<TEnum>(this string text)
        where TEnum : struct, IComparable, IFormattable, IConvertible 
    {
        TEnum result = default(TEnum);
        return !string.IsNullOrWhiteSpace(text) 
            && Enum.TryParse(text, true, out result)
            && Enum.IsDefined(typeof(TEnum), result.ToString())
                ? result
                : default(TEnum);
    }
}

这里,我创建了一个 .NET Fiddle ,清晰地展示了这一点。


如果您传递一个包含枚举中不存在的整数值的字符串,则此方法将失败。使用您的fiddle示例,尝试调用Console.WriteLine("55".ToEnum<ReportStatus>()) - Mass Dot Net

1
将文本与枚举字符串的大小写保持一致:
enum FileExts
{
  jpg,
  pdf
}

if (Enum.IsDefined(typeof(T), text.tolower())) //does not have ignoreCase parameter
    return (T)Enum.Parse(typeof(T), text, true);
else
    return default(T);

1
    enum DaysCollection
    {
     sunday,
     Monday,
     Tuesday,
     Wednesday,
     Thursday,
     Friday,
     Saturday
    }

    public bool isDefined(string[] arr,object obj)
    {
        bool result=false;
        foreach (string enu in arr)
        {
              result = string.Compare(enu, obj.ToString(), true) == 0;
              if (result)
                  break;

        }
        return result;


    }

    private void button1_Click(object sender, EventArgs e)
    {
        object obj = "wednesday";
        string[] arr = Enum.GetNames(typeof(DaysCollection)).ToArray();

        isDefined(arr,obj);
    }

1
首先使用 Enum.TryParse 方法获取一个类型为 T 的对象,然后将该对象传递给 Enum.IsDefined 方法:
private static T ConvertStringToEnum<T>(string stringValue) where T : struct
{
    if (System.Enum.TryParse(stringValue, out T result))
    {
        if (System.Enum.IsDefined(typeof(T), result) || result.ToString().Contains(","))
            return result;

        throw new System.Exception($"{stringValue} is not an underlying value of the {typeof(T).FullName} enumeration.");
    }

    throw new System.Exception($"{stringValue} is not a member of the {typeof(T).FullName} enumeration.");
}

除此之外,它不忽略大小写。 - Michel de Ruiter

0
public static T ConvertStringToEnum<T>(string text)
{
    T returnVal;
    try
    {
        returnVal = (T) Enum.Parse( typeof(T), text, true );
    }
    catch( ArgumentException )
    {
        returnVal = default(T);
    }
    return returnVal;
}

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