OCT
17
2010

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 it 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.

Before with System.Enum

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);

After with Enum<T>

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;

The class

GitHub has the latest version of EnumT.cs

Usage notes

  • 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 run-time lookup
  • Some of the method may not behave exactly like their Enum counterparts in terms of exception messages, nulls etc.

[)amien

0 responses

  1. Avatar for

    Information is only used to show your comment. See my Privacy Policy.