enkryptor
enkryptor

Reputation: 1663

What is the best way to strictly parse string to enum?

Let's say I have an enum:

public enum MyEnum
{
    OptionOne = 0,
    OptionTwo = 2,
    OptionThree = 4
}

Like it was said in the How should I convert a string to an enum in C#? question, I parse enum from string, using Enum.Parse method:

public class Enumer
{
    public static MyEnum? ParseEnum(string input)
    {
        try
        {
            return (MyEnum) Enum.Parse(typeof (MyEnum), input);
        }
        catch (ArgumentException)
        {
            return null;
        }
    }

}

Unfortunately, it doesn't work as expected with integer numbers, represented as strings.
I do not expect Parse.Enum() to convert int from string, but actually it does.
A simple test:

[TestClass]
public class Tester
{
    [TestMethod]
    public void TestEnum()
    {
        Assert.AreEqual(MyEnum.OptionTwo, Enumer.ParseEnum("OptionTwo"));
        Assert.IsNull(Enumer.ParseEnum("WrongString"));
        Assert.IsNull(Enumer.ParseEnum("2")); // returns 2 instead of null
        Assert.IsNull(Enumer.ParseEnum("12345")); // returns 12345 instead of null
    }
}

Only two checks of four can be passed.
Enumer.ParseEnum("2") returns MyEnum.OptionTwo instead of null.
Moreover, Enumer.ParseEnum("12345") returns 12345, regardless it's out of the scope.

What is the best way to parse:

  1. Only string values of "MyEnum.OptionOne", "MyEnum.OptionTwo", "MyEnum.OptionThree" into their enum counterparts.
  2. Strings "MyEnum.OptionOne", "MyEnum.OptionTwo", "MyEnum.OptionThree" AND the values of 0, 2 and 4 into MyEnum.OptionOne, MyEnum.OptionTwo, MyEnum.OptionThree respectively?

The link to the similar question How should I convert a string to an enum in C#? doesn't help with the test provided - it still converts strings as integers, even when they are out of the enum scope.

Upvotes: 7

Views: 3224

Answers (1)

Toxantron
Toxantron

Reputation: 2398

You can do this yourself using Enum.GetValues(Type enumType).

public static MyEnum? ParseEnum(string input)
{
    return (MyEnum?) Enum.GetValues(typeof(MyEnum)).OfType<object>()
                         .FirstOrDefault(v => v.ToString() == input);
}

Adding support for integers you can also add.

public static MyEnum? ParseEnum(string input)
{
    int value;
    var isInt = int.TryParse(input, out value);
    return (MyEnum?) Enum.GetValues(typeof(MyEnum)).OfType<object>()
                         .FirstOrDefault(v => v.ToString() == input
                                           || (isInt & (int)v == value));
}

Since I am not 100% sure about your last requirements I add one that includes the name as well and is more generic

public static T? ParseEnum<T>(string input)
    where T : struct 
{
    int value;
    var isInt = int.TryParse(input, out value);
    return (T?)Enum.GetValues(typeof(T)).OfType<object>()
                   .FirstOrDefault(v => v.ToString() == typeof(T).Name + input
                                    || (isInt & (int)v == value));
}

Upvotes: 6

Related Questions