During a recent project of mine, I had to do a lot of enum manipulation using the .NET Enum class and I have to say I wasn't impressed. Besides the shortcoming of it not being a generic class (and thus not being especially type-safe), Enum also has some strange inconsistencies in terms of how it handles matched integral types.

GetName

Why does GetName return null for any integral type you pass it except those that correspond to a constant?

For example, this is completely valid:

enum Fruit : byte { Apple, Orange, Cumquat };
var fruit = Enum.GetName( typeof(Fruit), ulong.MaxValue );
// fruit now equals null

In every other case in the Enum class where you pass an integral value outside the range of the underlying type, you get an exception. Except here.

IsDefined

Why does IsDefined throw an exception when you pass it an integral value that can be safely narrowed to the enumeration's underlying type?

For example, this will throw an exception:

enum Fruit : byte { Apple, Orange, Cumquat };
var defined = Enum.IsDefined( typeof(Fruit), 0 );

Why? Because it's a integer literal (and therefore type int) and the underlying type of Fruit is byte. This would be alright if this is how the Enum class consistently treated this case, but it's not. In every other method, the Enum class is OK with you passing in an int value for a byte enumeration (as long as it's within the range of the underlying type).

ToObject

Why does ToObject silently overflow for values outside the range of the enumeration's underlying type?

For example, this code will give the following non-intuitive result:

enum Fruit : byte { Apple, Orange, Cumquat };
var apple = Enum.ToObject( typeof(Fruit), 0 );
var elppa = Enum.ToObject( typeof(Fruit), 256 );
Assert.AreEqual(apple, elppa);

Assuming that we ignore the fact that silently overflowing is typically a bad idea, this is again completely inconsistent with the rest of the library. Why overflow here, but not in GetName?

Conclusion

The .NET Enum class is not lacking in it's ability to extract any information you need from an enumeration. It is, however, a little inconsistent in terms of how it treats underlying type mismatches, overflows and narrowing conversions. Barring a new, generified Enum class from Microsoft, the best way to deal with these inconsistencies is to either be aware of them, or use a third-party library to hide them.

(Edit: Some people have noted that my conclusion was a bit weak and preachy towards Dnum, which I have to admit is not without truth. I've seen edited it. For the record, the old conclusion was: "Fortunately, these and other problems have been fixed in my Dnum library, which should be released in the next couple days. Be sure to check it out.").