views:

1010

answers:

6

I have an enum type like this as an example:

public Enum MyEnum {
    enum1, enum2, enum3 };

I'll read a string from config file. What I need it to parse the string to MyEnum type or null o not defined. Not sure if the following codes will work (sorry for not having access to my VS right now):

// example: ParseEnum<MyEnum>("ENUM1", ref eVal);
bool ParseEnum<T>(string value1, ref eVal) where T : Enum
{
  bool bRet = false;
  var x = from x in Enum.GetNames(typeof(T)) where 
       string.Equals(value1, x, StringComparison. OrdinalIgnoreCase)
       select x;
  if (x.Count() == 1 )
  {
    eVal = Enum.Parse(typeof(T), x.Item(0)) as T;
    bRet = true;
  }
  return bRet;
}

Not sure if it is correct or there is any other simple way to parse a string to MyEnum value?

A: 

How about Enum.Parse(Type, String)?

llamaoo7
That's not going to tell him whether the string can be parsed. As I understand the question, he's looking for a TryParse sort of method.
Mark Seemann
@Mark. Thanks for your comments. I tried to find out TryParse from Enum but it is not available for Enum.
David.Chu.ca
+6  A: 

What about something like:

public static class EnumUtils
{
    public static Nullable<T> Parse<T>(string input) where T : struct
    {
        //since we cant do a generic type constraint
        if (!typeof(T).IsEnum)
        {
            throw new ArgumentException("Generic Type 'T' must be an Enum");
        }
        if (!string.IsNullOrEmpty(input))
        {
            if (Enum.GetNames(typeof(T)).Any(
                  e => e.Trim().ToUpperInvariant() == input.Trim().ToUpperInvariant()))
            {
                return (T)Enum.Parse(typeof(T), input, true);
            }
        }
        return null;
    }
}

Used as:

MyEnum? value = EnumUtils.Parse<MyEnum>("foo");

(Note: old version used try/catch around Enum.Parse)

Rex M
It's not a good idea to use exception handling in normal program flow...
Mark Seemann
@Mark I know, but it sure is easy in cases like this :)
Rex M
@Mark does make a good point - try/catch is rarely a good idea. The edited version is considerably more efficient than the one in the question, as it only does one partial enumeration instead of up to 3-ish.
Rex M
A generic type constraint to ensure T : Enum would be useful here too, otherwise Enum.GetNames may throw.
Ty
@Ty unfortunately that's not possible - type constraint can't be enum.
Rex M
Oh really? Guess it wouldn't be quite so useful after all then! My bad.
Ty
any way to find out if T's base class is Enum? so that it would avoid exception?
David.Chu.ca
One enhancement is to change input.ToUpperInvariant() to (input ?? "").Trim(). ToUpperInvariant(). This will avoid null exception.
David.Chu.ca
@Rex M, you may add @Benjol suggestion of type.IsEnum to check the type in if statement.
David.Chu.ca
@David good idea. Added!
Rex M
+3  A: 
private enum MyEnum
{
    Enum1 = 1, Enum2 = 2, Enum3 = 3, Enum4 = 4, Enum5 = 5, Enum6 = 6, 
    Enum7 = 7, Enum8 = 8, Enum9 = 9, Enum10 = 10
}

private static Object ParseEnum<T>(string s)
{
    try
    {
        var o = Enum.Parse(typeof (T), s);
        return (T)o;
    }
    catch(ArgumentException)
    {
        return null;
    }
}

static void Main(string[] args)
{
   Console.WriteLine(ParseEnum<MyEnum>("Enum11"));
   Console.WriteLine(ParseEnum<MyEnum>("Enum1"));
   Console.WriteLine(ParseEnum<MyEnum>("Enum6").GetType());
   Console.WriteLine(ParseEnum<MyEnum>("Enum10"));
}

OUTPUT:

    //This line is empty as Enum11 is not there and function returns a null
Enum1
TestApp.Program+MyEnum
Enum10
Press any key to continue . . .
TheVillageIdiot
A: 

If you're using .NET 3.5 (or even 2.0, if you trim out the extension method), I've had great luck with the techniques in this article:

Enumerations and Strings - Stop the Madness!

Chris Doggett
+2  A: 

I have a TryParseName method in UnconstrainedMelody, a library for delegate and enum utility methods which uses "inexpressible" constraints via some postbuild trickery. (Code using the library doesn't need a postbuild, just to be clear.)

You would use it like this:

Foo foo;
bool parsed = Enums.TryParseName<Foo>(name, out foo);

I don't currently have a case-insensitive version, but I could easily introduce one if you wanted. Note that this doesn't try to parse numbers e.g. "12" like the built-in version does, nor does it try to parse comma-separated lists of flags. I may add the flags version later on, but I can't see much point in the numeric version.

This is done without boxing and without execution time type checking. Having the constraint is really handy :)

Please let me know if you'd find a case-insensitive parse useful...

Jon Skeet
@Jon Skeet, you may introduce additional overloaded method to let user to have choices of case-sensitive or not.
David.Chu.ca
@Joh Skeet, not sure if your method should be (name, ref foo) or not. If tryparse fails, what should foo be? The first enum value? I think is better to let user to initialize it and not change it if fails. I understand you try to make this method being consistent with TryParse(name, out value).
David.Chu.ca
If `TryParse` fails, it will be `default(Foo)` which is consistent with `TryParse`, `TryGetValue` etc. If I weren't going for consistency I'd probably return a `Nullable<T>` instead. I'll look at introducing a new overload for case-insensitive matching - or possibly taking a StringComparer (or similar) to allow cultural sensitivity to be selected too.
Jon Skeet
+1  A: 

I have just combined the syntax from here, with the exception handling from here, to create this:

public static class Enum<T>
{
    public static T Parse(string value)
    {
        //Null check
        if(value == null) throw new ArgumentNullException("value");
        //Empty string check
        value = value.Trim();
        if(value.Length == 0) throw new ArgumentException("Must specify valid information for parsing in the string", "value");
        //Not enum check
        Type t = typeof(T);
        if(!t.IsEnum) throw new ArgumentException("Type provided must be an Enum", "TEnum");

        return (T)Enum.Parse(typeof(T), value);
    }
}

You could twiddle it a bit to return null instead of throwing exceptions.

Benjol
@Benjol, I like your using type.IsEnum to check its type.
David.Chu.ca