GOTCHA 22 enum lacks type-safety


GOTCHA #22 enum lacks type-safety

enum provides convenience and improved productivity. The possible values get listed in IntelliSense, so it's easy to select the one you want during programming. If your method takes an enum as a parameter, the users of your API will typically select a value from the list presented by IntelliSense. But unfortunately they don't have to, which could lead to code like that shown in Example 3-5. In this program, Method1() receives an enum and accesses the array resource based on that value. First, you pass three valid values of the Size enum to Method1(). Then, you pass an invalid value of 3. The output is shown in Figure 3-3.

Example 3-5. Example to study type-safety of enum

C# (EnumSafety)

 using System; namespace EnumTypesafety {     class Program     {          private static int[] resource = new int[] {0, 1, 2};         public enum Size         {             Small,             Medium,             Large         }         public static void Method1(Size theSize)         {             Console.WriteLine(theSize);             Console.WriteLine("Resource: {0}",                 resource[(int)theSize]);         }         [STAThread]         static void Main(string[] args)         {             Method1(Size.Small);             Method1(Size.Large);             Method1((Size) 1);             Method1((Size) 3);         }     } } 

VB.NET (EnumSafety)

 Module Program     Private resource() As Integer = New Integer() {0, 1, 2}     Public Enum Size         Small         Medium         Large     End Enum     Public Sub Method1(ByVal theSize As Size)         Console.WriteLine(theSize)         Console.WriteLine("Resource: {0}", _              resource(Convert.ToInt32(theSize)))     End Sub      Sub Main()         Method1(Size.Small)         Method1(Size.Large)         Method1(CType(1, Size))         Method1(CType(3, Size))     End Sub End Module 

So what happens if the value sent in for the enum does not match one of the permissible values? At compile time, no error or warning is reported. Users are allowed to send Method1() an invalid value of 3 for the enum.

What's going on here? The answer lies in the translation to MSIL. The MSIL generated from the above code is shown in Figure 3-4.

Figure 3-3. Output from Example 3-5


Figure 3-4. MSIL for the Main method in Example 3-5


There is no difference between passing one of the correct values and one of the incorrect ones. Under the hood, there is no type safety or range checking in place. That's why you get a runtime exception instead of a compile-time error when you access the array with the index provided. Too bad the compiler does not catch this or even give you a warning.

What can you do about this? Within methods that receive an enum, make sure the given value is valid. You have to do this before using an enum parameter to make your code robust. A modified Method1() that takes care of this checking is shown in Example 3-6, along with a Main() modified to catch the thrown exception. The output is shown in Figure 3-5.

Example 3-6. Example of type-safe usage of enum

C# (EnumSafety)

 using System; namespace EnumTypesafety {     class Program     {         private static int[] resource = new int[] {0, 1, 2};         public enum Size         {             Small,             Medium,             Large         }         public static void Method1(Size theSize)         {              if(System.Enum.IsDefined(typeof(Size), theSize))             {                 Console.WriteLine(theSize);                 Console.WriteLine("Resource: {0}",                     resource[(int)theSize]);              }             else             {                 throw new ApplicationException(                         "Invalid input for Size");             }         }         [STAThread]         static void Main(string[] args)         {              try             {                 Method1(Size.Small);                 Method1(Size.Large);                 Method1((Size)(1));                 Method1((Size)(3));              }             catch(ApplicationException ex)             {                 Console.WriteLine(ex.Message);                 Console.WriteLine(ex.StackTrace);             }         }     } } 

VB.NET (EnumSafety)

 Module Program     Private resource() As Integer = New Integer() {0, 1, 2}     Public Enum Size         Small         Medium         Large     End Enum     Public Sub Method1(ByVal theSize As Size)          If System.Enum.IsDefined(GetType(Size), theSize) Then             Console.WriteLine(theSize)             Console.WriteLine("Resource: {0}", _                 resource(Convert.ToInt32(theSize)))          Else             Throw New ApplicationException( _                 "Invalid input for Size")         End If     End Sub     Sub Main()          Try             Method1(Size.Small)             Method1(Size.Large)             Method1(CType(1, Size))             Method1(CType(3, Size))          Catch ex As ApplicationException             Console.WriteLine(ex.Message)             Console.WriteLine(ex.StackTrace)         End Try     End Sub End Module 

Figure 3-5. Output after the modifications in Example 3-6


This code verifies that the enum value you've been passed is valid by calling System.Enum.IsDefined(). If it is not, it throws an exception. This prevents you from accessing the resource array with an invalid index. While using System.Enum.IsDefined() to verify the enum value may appear logical, there are some inherent problems in using it. First, the call to IsDefined() is expensive, as it relies heavily on reflection and metadata. Second, IsDefined() checks if the value is one of the possible values, not necessarily the ones you expect. This will be a problem if a new value is added to the enum during versioning. Refer to http://blogs.msdn.com/brada/archive/2003/11/29/50903.aspx for more details on this.

Another problem with enum types relates to serialization. The deserialization of an enum may break if you change a value in the enum after serializing an object.

IN A NUTSHELL

Do not assume that the value of an enum received as a method parameter is within range. Check to verify it. This will make your code more robust. Program defensively.

SEE ALSO

Gotcha #15, "rethrow isn't consistent," and Gotcha #16, "Default of Option Strict (off) isn't good."



    .NET Gotachas
    .NET Gotachas
    ISBN: N/A
    EAN: N/A
    Year: 2005
    Pages: 126

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net