简体   繁体   中英

How can I test if an enum is defined or not whilst ignoring case?

The following generic static method takes a string and returns an enum .

It nicely ignores case since I set the ignoreCase parameter to true.

However, I also want to test if the enum exists , but the enum.IsDefined method to do this doesn't seem to have an ignoreCase parameter.

How can I test if the enum is defined or not and at the same ignore case?

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

Along with @Darin's answer, in .NET 4.0, the Enum type now has a TryParse method:

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

The important thing to remember is that there is a fundamental difference in the behaviour of Parse vs TryParse. Parse methods will throw exceptions. TryParse methods will not. This is quite important to know if you are potentially trying to parse many items.

You might be able to get away with simply using Enum.TryParse , as others have said.

However, if you want a more robust/general conversion that allows you to convert more than just strings, then you need to also use Enum.IsDefined , which unfortunately, as you found, is not case-insensitive.

Enum.TryParse is (can be) case-insensitive. But unfortunately, it allows out-of-range ints to get through!

So the solution is to use them together (and the order is important).

I wrote an extension method that does just that. It allows conversion from string, int/int?, and any other Enum/Enum? type like so:

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

Here's the heart of the method. This is the conversion part that answers your question. The variable value is of type object because of the "overloads" I have that take different types as the main input (see above), but you can do this with a variable of type string just fine if that's all you want (obviously changing value.ToString() to just 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;
        }
    }
}

There's a lot more to my extension method... it allows you to specify defaults, handles out-of-range ints just fine, and is fully case-insensitive. I can post more of it if anybody's interested.

I'm using Compact Framework 3.5, and:

Enum.TryParse

...doesn't exist. It does have:

Enum.IsDefined

..but that doesn't support the ignoreCase parameter. I'd like the best of both worlds, so came up with this (as a helper method)...

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

Use Enum.TryParse instead:

T val;

if(Enum.TryParse(text, true, out val))
    return val;
else 
    return default(T);
    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);
    }

Make text same case as enum string:

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

I had a similar concern and used a combination of both the .Enum.TryPase (with the case-insensitive flag set as true ) and Enum.IsDefined . Consider the following as a simplification to your helper class:

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

And while we're at it, since the helper class is static and the method is static - we could make this an extension method on 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);
    }
}

Here, I created a .NET Fiddle that clearly demonstrates this.

First use Enum.TryParse method to get an object of type T , then pass that object to Enum.IsDefined method:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM