Enums in .NET – part III

Since a C# enum declaration is always “transformed” into a struct which inherits from the Enum type by the compiler, then we can reuse those instance methods inherited from Enum and the static ones which expect Enum parameters. In this post, we’ll be looking at the static methods defined by Enum. Lets start by introducing a simple enum which we’ll reuse through the rest of this post:

public enum Sizes:long {
    XSmall=10,
    Small,
    Medium=20,
    Large=30,
    XLarge
}

One of the things you might need is get an enum’s underlying type, which can be done through the static GetUnderlyingType method:

Enum.GetUnderlyingType( typeof(Sizes) )

It’s also possible to enumerate the names and values of an enumerated type by using the GetValues and GetNames methods:

var names = Enum.GetNames( typeof( Sizes ) ); //get names
foreach( var name in names ) {
    Console.WriteLine( name );
}
var values = Enum.GetValues( typeof( Sizes ) ); //get values
//notice the use of Int64 or you'll get the name instead of the value
foreach( Int64 value in values ) {
    Console.WriteLine( value );
}

You’ve probably noticed the Int64 cast…if you don’t do that, you’ll end up with a string with symbolic name instead. This happens because the GetValues method returns an array which contains one element for each symbolic name of the enum, where each element is an object with the corresponding boxed enum symbolic name’s value.

The Enum class will also let you get the symbolic name of a value by using the static GetName method:

Console.WriteLine(Enum.GetName( typeof(Sizes), 30 ));// Large

If you’ve got a string, you can try to map it into an enum’s symbolic name through the static Parse method:

var value = Enum.Parse( typeof( Sizes ), "large", true );//30
Console.WriteLine((Int64)value);// 30

In the previous snippet, I’ve used the overload which accepts a Boolean that specifies if the matching should be done in a case insensitive manner (true in the previous example). If Parse can’t match the string to a symbolic name, then it will simply generate an exception. If this behavior is not acceptable, then you should rely on the TryParse method instead.

You can also use the IsDefined method if you want to check if an enum supports a specific value or symbolic name:

Console.WriteLine(Enum.IsDefined( typeof(Sizes), 30L ));// true
Console.WriteLine(Enum.IsDefined( typeof(Sizes), 1L ));// false
Console.WriteLine(Enum.IsDefined( typeof(Sizes), "Large" ));// true
Console.WriteLine(Enum.IsDefined( typeof(Sizes), "large" ));// false

As you can see, we can pass a value or a string that identifies a symbolic name. When we pass a value, we must ensure that it has the same type as the one used as the enum’s underlying type. If you don’t do that, then you’ll end up with an exception. Notice also that, unlike Parse, there’s no overload for specifying that the symbolic name matching should be done in a case insensitive manner. Too bad, but that’s life…There’s also a small penalty associated with the internal use of reflection…Finally, it’s important to notice that the method might not work as expected when you pass it a value from a flags enum (where you’ve combined two values). Bottom line: it’s probably a good idea to stay away of this method…

Finally, there’s one more static method: ToObject. There are several overloads of this method (that differ in the expected parameter type) which allow you to convert a value into an enumerated value:

var aux = 30L;
var converter = Enum.ToObject( typeof( Sizes ), aux );
Console.WriteLine(aux.GetType(  ));//Int64
Console.WriteLine(converter.GetType(  ));//Sizes

The most interesting thing about this method is that it allows us to pass values of a different type used for the enum’s underlying type. Do notice that there’s also a nasty problem which might get you off guard:

var aux = 230L;
var converted = Enum.ToObject( typeof( Sizes ), aux );
Console.WriteLine(aux.GetType(  ));//Int64
Console.WriteLine(converted.GetType(  ));//Sizes
Console.WriteLine(Enum.IsDefined(typeof(Sizes),converted));//false

Wow! wtf? Yes, it works. you end up getting a Sizes enum whose value doesn’t have an associated symbolic name. I believe that there are some reasons for allowing this. First, I’m not sure if checking the value would be a reasonable thing to do from a performance point of view. Second, what would be the correct behavior for flag enums? Btw, there’s a similar problem when you initialize a Size enum variable with its default value:

Sizes a = default(Sizes);
Console.WriteLine(a);//0!

Yes, Default(Sizes) produces a Sizes enum value set to 0. So, if you’re really interested in ensuring that you’ve got a “valid” enum value, don’t forget to check it before converting it into an enum (you can use the IsDefined method or create your own custom routine for doing that check). And I guess this is all for now. In the next post, we’ll take a quick dive into the instance methods inherited from Enum. Stay tuned for more!

Advertisements

~ by Luis Abreu on June 8, 2011.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: