That pesky enum

The .NET enum, such a simple concept.  At the most basic level an enum is a collection of key/value pairs with a restricted string key and an integer value. However, the enum can trick you.  It would seem logical that when you define a new enum it'd obey the same rules as would any class you define.  That assumption can lead to subtle and sometimes difficult to identify issues.

To illustrate the point I'd like to make I'm going to define a new enum called Beers and then define a new class called BeersMockEnum who's purpose will be to mock the behavior of the Beers enum.  Utilizing these I'll illustrate how they differ in a way that can present significant problems in real life implementation and deployment scenarios.

First I'll define the Beers enum.

public enum Beers  
{
    IPA = 0,
    Lager,
    Porter,
    Stout 
} 

Using the following I'll iterate over the values and output.

foreach (var value in Enum.GetValues(typeof(Beers)))  
{
    Console.WriteLine("{0}={1}", value, (int)value); 
} 

Naturally this yields the console output below.  No surprises so far.

Now I'll define BeersMockEnum.  I've defined it as a static class to mock the way an enum is utilized in code.  The purpose of this class is not to provide functionality that is 100% identical to an enum but to help me illustrate how enums differ from other .NET types.

public static class BeersMockEnum  
{
    public static int IPA { get { return 0; } }
    public static int Lager { get { return 1; } }
    public static int Porter { get { return 2; } }
    public static int Stout { get { return 3; } } 
} 

Iteration over BeersMockEnum values is a little more verbose but yields the same results.

foreach (var value in typeof(BeersMockEnum).GetProperties(BindingFlags.Public | BindingFlags.Static))  
{
    Console.WriteLine(
        "{0}={1}", 
        value.Name, value.GetValue(typeof(BeersMockEnum), 
        null)); 
}

Not surprisingly this yields the identical console output (which I've excluded for simplicity).
For this test case I have a very simple solution with just two projects, ConsoleApplication and ClassLibrary.  The Beers enum and the BeersMockEnum static classes are both contained in the ClassLibrary project.  In addition I've added one additional static class to that project called Informer.  The purpose of Informer is to report on a specific value from within the ClassLibrary project.  The Informer class is defined as follows.

public static class Informer  
{
    public static string GetLagerInfo()
    {
        return String.Format(
                "Beers.Lager has a value of {0}", 
                (int)Beers.Lager); 
    }

    public static string GetMockLagerInfo() 
    { 
        return String.Format(
               "BeersMockEnum.Lager has a value of {0}", 
               BeersMockEnum.Lager); 
    } 
} 

Next, I've added code to the Main method of the static Program class in the ConsoleApplication project whose purpose is to simply output value information for the Lager entries in both the Beers enum and the BeersMockEnum.  This information is being retrieved in two ways.  First, I access the types directly from the ConsoleApplication.  Second, I access the types using the Informer class in the ClassLibrary project.  The code to generate the output is as follows.

Console.WriteLine("---Beers---");  
Console.Write("From Main: ");  
Console.WriteLine(String.Format(  
                   "Beers.Lager has a value of {0}", 
                   (int)Beers.Lager)); 

Console.Write("From Informer: ");  
Console.WriteLine(Informer.GetLagerInfo()); 

Console.WriteLine(); 

Console.WriteLine("---BeersMockEnum---");  
Console.Write("From Main: ");  
Console.WriteLine(String.Format(  
                  "BeersMockEnum.Lager has a value of {0}",
                  BeersMockEnum.Lager)); 
Console.Write("From Informer: ");  
Console.WriteLine(Informer.GetMockLagerInfo());  

The output is what you'd expect.

Whether the values are looked up from the Program class in the ConsoleApplication or through use of the Informer class in the ClassLibrary they are the same.

Due to a silly limit on the size of a single blog entry I am forced to break this topic into two separate posts.  Therefore, in my next post