Enums – Better syntax, improved performance and TryParse in NET 3.5
Recently I needed to map external data into in-memory objects. In such scenarios the TryParse methods of Int and String are useful but where is Enum.TryParse? TryParse exists in .NET 4.0 but like a lot of people I’m on .NET 3.5.
A quick look at Enum left me scratching my head.
- Why didn’t enums receive the generic love that collections received in .NET 2.0?
- Why do I have to pass in typeof(MyEnum) everywhere?
- Why do I have to the cast results back to MyEnum all the time?
- Can I write TryParse and still make quick – i.e. without try/catch?
I found myself with a small class, Enum<T> that solved all these. I was surprised when I put it through some benchmarks that also showed the various methods were significantly faster when processing a lot of documents. Even my TryParse was quicker than that in .NET 4.0.
While there is some small memory overhead with the initial class (about 5KB for the first, a few KB per enum after) the performance benefits came as an additional bonus on top of the nicer syntax.
var getValues = Enum.GetValues(typeof(MyEnumbers)).OfType(); var parse = (MyEnumbers)Enum.Parse(typeof(MyEnumbers), "Seven"); var isDefined = Enum.IsDefined(typeof(MyEnumbers), 3); var getName = Enum.GetName(typeof(MyEnumbers), MyEnumbers.Eight); MyEnumbers tryParse; Enum.TryParse<MyEnumbers>("Zero", out tryParse);
var getValues = Enum<MyEnumbers>.GetValues(); var parse = Enum<MyEnumbers>.Parse("Seven"); var isDefined = Enum<MyEnumbers>.IsDefined(MyEnumbers.Eight); var getName = Enum<MyEnumbers>.GetName(MyEnumbers.Eight); MyEnumbers tryParse; Enum<MyEnumbers>.TryParse("Zero", out tryParse);
I also added a useful ParseOrNull method that lets you either return null or default using the coalesce so you don’t have to mess around with out parameters, e.g.
MyEnumbers myValue = Enum<MyEnumbers>.ParseOrNull("Nine-teen") ?? MyEnumbers.Zero;
GitHub has the latest version of EnumT.cs
- This class as-is only works for Enum’s backed by an int (the default) although you could modify the class to use longs etc.
- I doubt very much this class is of much use for flag enums
- Casting from long can be done using the CastOrNull function instead of just putting (T)
- GetName is actually much quicker than ToString on the Enum… (e.g. Enum<MyEnumbers>.GetName(a) over a.ToString())
- IsDefined doesn’t take an object like Enum and instead has three overloads which map to the actual types Enum.IsDefined can deal with and saves runtime lookup
- Some of the method may not behave exactly like their Enum counterparts in terms of exception messages, nulls etc.