.NN 22: Enums Can be Cast to Unmapped values
There are a lot of little nuances with the .NET Framework and one such nuance I see quite a bit of is an interesting behavior of enumerations. Did you know you could cast an instance of an enum to a value that isn’t in the enumeration?
Let’s take the following enum and method:
public enum Gender {
Male = 0,
Female = 1
}
public static void WriteGender(Gender gender)
{
Console.WriteLine(gender.ToString());
}
Somewhere in code someone will call the method like this:
WriteGender(Gender.Male);
This will result in the word “Male” being written to the console, just as you would expect; however, what if you were using the enumeration as a property on a person class and loaded that class from data in the database?
public class Person {
// …
public Gender Gender {get; set; }
// …
}
When you load the class from your data source you will likely cast the value you stored for Gender, but that’s where the problem lies. In the data store the value is likely an Integer (of some size) and could be values other than zero and one. Using the method and enumeration above, try the following in code:
Gender testGender = (Gender)3;
WriteGender(testGender);
This code compiles and executes just fine. The result is the number 3 is written to the console. If you aren’t careful about this it can lead to some unintentional consequences and bugs. For example, it wouldn’t be surprising if a developer created an if or switch statement around the value of the Gender property. Likely they will expect one of the two values and not code for anything other than that. I’ve seen similar scenarios where someone created a switch statement and only dealt with the two known cases, and didn’t supply a default statement. This lead to the system silently going forward without an exception and a decently hard bug to track down later down the line.
The lesson to learn here is that you should ALWAYS ensure you have guard clauses when dealing with enums (well, at least that is my opinion). If you are using an enum as a parameter to a method, check the validity of the value being passed to you. If you are loading an enumeration value on an object, again, check to make sure it is valid.
You can check for validity by using the Enum.IsDefined method.
if (!Enum.IsDefined(typeof(Gender), gender)){
//… The value isn’t mapped in the enum!
}
What you decide to do with the knowledge that an invalid value is being passed is, of course, dependant entirely on what your application needs to do. Sometimes you may then map the value to a default, or maybe you’ll throw an exception. How you decide to handle it is really not as important as knowing that you should be handling it.
As with any public method parameter, check your enumerations. You should have guard clauses on most parameters that are passed in to your public methods simply because you should be following the tenant of “don’t trust your callers”. This is especially true if the data originates from a 3rd party system or from a user interface.
Until next time…