Monday, March 22, 2010

Typesafe enumerations in .NET

One of the weaker aspects of C# and VB.NET is the lack of support for strongly-typed enumerations. The enum type in VB.NET uses whole numbers as a backing store with Integer being the default backing store type. Since enums are a value type based on whole numbers, they can have mathematical operations applied to them. You can, for example, add or subtract to their value. More importantly, you can assign a value to an enum that is not actually part of the enum. As programmers we make an association between the enum and backing store and face the temptation of dealing with the underlying store and explicitly assigning a value. This applies even to code that could be identified as buggy at compile-time. It is possible to identify whether an enum has a valid value by using the System.Enum.IsDefined function.


Here is an example of a convention enum that illustrates the weaknesses of the Enum type.


 Enum ConnectionStatus  
Closed
Open
Retrying
End Enum
Sub BuiltInEnumTest()
Dim connectionType = ConnectionStatus.Open
Console.WriteLine()
connectionType += 5
If [Enum].IsDefined(GetType(ConnectionStatus), connectionType) = False Then
Console.WriteLine("Out of bounds value")
End If
End Sub



One of the many takeaways I gained from a course with JP Boodhoo (his famous Nothing But Net Course) was how to create what can be treated like a typesafe enum. The idea is to have a class that equates to the enum type with static variables representing instances of objects that are the value of the enum. In my version, I add a private constructor so that an instance of the enum type can’t be created outside of the class. By removing the integer-based backing store, programmers are forced to treat the enum type as a black box. This ensures type-safety. Here is how this looks converted to a type-safe enum.


 Enum ConnectionStatus  
Closed
Open
Retrying
End Enum
Sub BuiltInEnumTest()
Dim connectionType = ConnectionStatus.Open
Console.WriteLine()
connectionType += 5
If [Enum].IsDefined(GetType(ConnectionStatus), connectionType) = False Then
Console.WriteLine("Out of bounds value")
End If
End Sub



There is one drawback worth mentioning. As a consequence of removing the integer-based backing store it is no longer possible to use the values in a Select statement in VB.NET 10. It is under consideration, however, for future versions of VB.NET to allow a select statement that allows type to be the selection criterion.

No comments:

Post a Comment