That pesky enum (part 2)
This is part 2 of my previous That pesky enum post. You should read part 1 first. In part 1 I defined the enum Beers and a static class BeersMockEnum and illustrated their use.
Now, I make modifications to these types by inserting a new item (Bitter) in the Beers enum and the BeersMockEnum class as follows.
public enum Beers
{
IPA = 0,
Bitter,
Lager,
Porter,
Stout
}
public static class BeersMockEnum
{
public static int IPA { get { return 0; } }
public static int Bitter { get { return 1; } }
public static int Lager { get { return 2; } }
public static int Porter { get { return 3; } }
public static int Stout { get { return 4; } }
}
I then recompile only the ClassLibrary project (which holds these definitions) and deploy the newly compiled assembly to the run directory replacing the existing ClassLibrary.dll but not the existing ConsoleApplication.exe. If I then run the .exe I will get the following result.
Note that the value of Beers.Lager is now incorrect when accessed from the Program class in the ConsoleApplication project but is correct if accessed through the use of the Informer class in the ClassLibrary project. BeersMockEnum, on the other hand, behaves as you'd expect. What this illustrates is that enums have their information compiled into the assemblies in which they are referenced. This is completely different than other .NET types which are loaded from their container assembly at runtime. What this means in practical terms is that if you modify an enum in one assembly, that enum is used by classes in another assembly and you only deploy the first assembly the enum references in the second assembly will still use the out of date version of the enum. This is very counter intuitive due to the fact that nothing else in .NET seems to function this way.
At this point you may be saying, "Wait a minute. Why would you recompile and deploy one assembly without also recompiling and deploying all assemblies that reference it?" I agree that a complete re-deployment of all assemblies is a "best practice" but legitimate, real-world, cases do occur in which such re-deployment is not practical. This is exactly the situation in which I first encountered this behavior.
The "solution" to this issue is to wrap some convention around the modification of enums. There are two possibilities that suggest themselves in preventing the wrong value from being reported by code that was compiled against a previous version of an enum.
- Never "insert" enum values in the middle of existing values but instead always add them to the end.
- Always specify values for each item in the enum and pad those values so that future entries can be "inserted" in between, e.g. 10, 20, 30, etc.