Thomas Ayoub
Thomas Ayoub

Reputation: 29451

Enum.Parse() vs switch performance

Everything is in the title, I've read this question Enum.Parse() or Switch but there is nothing about performance. My Enum is around 10 members long, I'd like to know which one is faster, switch or Enum.Parse() ?

Upvotes: 4

Views: 7658

Answers (6)

wonderik
wonderik

Reputation: 91

I've built a fast "switch" version using expression trees.

public static Func<String, TEnum> GetParseEnumDelegate<TEnum>()
{
  var eValue = Expression.Parameter(typeof(String), "value"); // (String value)
  var tEnum = typeof(TEnum);

  return
    Expression.Lambda<Func<String, TEnum>>(
      Expression.Block(tEnum,
        Expression.Switch(tEnum, eValue,
          Expression.Block(tEnum,
            Expression.Throw(Expression.New(typeof(Exception).GetConstructor(Type.EmptyTypes))),
            Expression.Default(tEnum)
          ),
          null,
          Enum.GetValues(tEnum).Cast<Object>().Select(v => Expression.SwitchCase(
            Expression.Constant(v),
            Expression.Constant(v.ToString())
          )).ToArray()
        )
      ), eValue
    ).Compile();
}
...
var parseEnum = GetParseEnumDelegate<YourEnum>();
YourEnum e = parseEnum("SomeEnumValue");

If you need a non-generic version, you must convert the result of the switch from tEnum to typeof(Object).

public static Func<String, Object> GetParseEnumDelegate(Type tEnum)
{
  var eValue = Expression.Parameter(typeof(String), "value"); // (String value)
  var tReturn = typeof(Object);

  return
    Expression.Lambda<Func<String, Object>>(
      Expression.Block(tReturn,
        Expression.Convert( // We need to box the result (tEnum -> Object)
          Expression.Switch(tEnum, eValue,
            Expression.Block(tEnum,
              Expression.Throw(Expression.New(typeof(Exception).GetConstructor(Type.EmptyTypes))),
              Expression.Default(tEnum)
            ),
            null,
            Enum.GetValues(tEnum).Cast<Object>().Select(v => Expression.SwitchCase(
              Expression.Constant(v),
              Expression.Constant(v.ToString())
            )).ToArray()
          ), tReturn
        )
      ), eValue
    ).Compile();
}

I did several perf tests and this delegate version was almost as fast as the native switch. In my scenario (enum with 10 items) it was 5 times faster than Enum.Parse().

Upvotes: 9

Darragh
Darragh

Reputation: 2656

Nice and fast:

public static class EnumHelpers<TTarget>
{
    static EnumHelpers()
    {
        Dict = Enum.GetNames(typeof(TTarget)).ToDictionary(x => x, x => (TTarget)Enum.Parse(typeof(TTarget), x), StringComparer.OrdinalIgnoreCase);
    }

    private static readonly Dictionary<string, TTarget> Dict;

    public static TTarget Convert(string value)
    {
        return Dict[value];
    }
}

Upvotes: 16

Jim Mischel
Jim Mischel

Reputation: 134065

In isolation, the switch will be faster.

But code rarely executes in isolation. Typically, the user will have entered a value, or you will have loaded it from a disk file. The whole purpose of Enum is to have a symbolic representation of a string value so that you can work with it more easily. It also allows you to validate the data that's entered.

So, let's say you have an enumeration:

public enum Thing
{
    Foo,
    Bar,
    Fooby,
    Barby
}

And somebody gives you a string that's supposed to represent one of those. You write a switch statement:

switch (s)
{
    case "Foo": break;
    case "Bar": break;
    case "Fooby": break;
    case "Barby": break;
    default : throw new ArgumentException();
}

What happens if the user entered "foo"? Are you going to reject it because the case isn't correct? Very unfriendly behavior. So you modify your code to do a ToLower() on the string, and modify your cases to use all lower case.

It's crazy. Your code will be much easier to understand and maintain if you use Enum.TryParse to convert it to an enum, and then do what needs to be done using the enum value.

Upvotes: 1

Faris Zacina
Faris Zacina

Reputation: 14274

As @CaldasGSM answered, the biggest problem is the reflection that is happening inside the Enum.Parse method. In addition just the number of IF's used in the Enum.Parse internal implementation is much higher than the number of conditions in your switch statement, not to count the other code inside that method. All of that makes it much inferior to a switch.

If you are processing a small number of items like you said than there is actually no significant difference if you are using Enum.Parse vs switch. For a large number of items it is a different story.

However, i would add that one more problem with Enum.Parse is that you have to handle three exception types using try-catch blocks in cases your parsing doesn't work, which will slow down your code as well.

In addition you should also not overlook the cost of boxing the enum values in the object type when using Enum.Parse, which is also a performance penalty.

To resolve the mentioned issues a much better alternative is to use the newer Enum.TryParse API, which will make it simpler to handle errors, and also prevent the boxing to object, since it uses generics.

Here is an example:

Items item;
if (!Enum.TryParse("First", true, out item))
{
    // Handle error
}

Upvotes: 1

1911z
1911z

Reputation: 1093

Use a switch (I prefer using parse anyway because of it's more maintainable).

Anyway, premature optimization is the root of all evil.

Upvotes: 0

CaldasGSM
CaldasGSM

Reputation: 3062

switch is always faster, since parse uses reflection to get the names of the members.. but unless the application is performance critical.. (thousands of executions per second).. using Enum.Parse makes code easier to maintain

Upvotes: 11

Related Questions