Reputation: 29451
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
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
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
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
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
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
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