tags:

views:

526

answers:

4

As we know, all enums are compiled as constants, which means you can get unexpected results if you use an enum from a different assembly.

Settings sequential enum numbers explicitly is no help, because if you want to leave room for new values, you have to do basic-linenumber-style spacing, which is naff.

So I wondered about using the hashcode of the Enum value name as string, which is fairly easy to generate a script for.

Is this a good idea? Will I have problems with the negative values that hashcode can return?

EDIT For those asking why, I quote the linked "corner case":

Given an assembly with:

enum MyEnum
{
    Red,
    Blue,
}

if you use MyEnum.Red.ToString() in another assembly, and in between times someone has recompiled your enum to:

enum MyEnum
{
    Black,
    Red,
    Blue,
}

at runtime, you will get "Black".

(NB, I am not especially interested in the ToString here, the problem is that if calling code asks for MyEnum.Red, it's actually referencing MyEnum.Black)

Another example to illustrate the problem with explicit sequential numbering:

enum ECountry
{
    Afghanistan = 1,
    Barbados = 2,
    Chile = 3,
    ...
    Zambia = 243, 
}

If Giggleswick suddenly declares itself a republic, what do you do?

+1  A: 

You won't have problems with negative values. You might have a problem with duplicate values, since the hashcode for a string isn't guaranteed to be unique.

Though, I'd rather just set the values explicitly to 1, 2, 3 and so.

Arjan Einbu
A: 

The hashcode of two different string values is not guaranteed to be unique, so that seems like a dangerous option. What is the problem with setting the values explicitly? Unless the numbers have a meaning in your object model then there would seem to be no reason to leave room for new values - just add new options onto the end of the enum.

Steve Willcock
+2  A: 

No, it's not a good idea.

You can't safely recreate a string from it's hash code. You can loop through the names and compare their hash code with the one that you have, but there is still a small risk of a false positive due to hash code collisions. The risk is rather small, but it's still not a reliable method to use.

Just use the name of the enum value if you need to convert from one version of an enum to another. The ToString method gives you the name, and you can use the Enum.Parse method to get the enum value from the string.

Edit:
Using the string value of course requires that you have one occurance of the enum in each assembly so that it's compiled each time. If you are using an enum in a referenced assembly, then you have to give each value a specific numeric representation, otherwise you don't have anything at all that keeps the value consistent from one version to the next.

Guffa
Thanks for the info about collisions. I have yet to decide how small the risk is likely to be. As I mention in my update, the ToString method doesn't necessarily give you the name you expect...
Benjol
If the enum is not in the same assembly, then you are basically screwed if you don't have specific numerical values and change the enum without recompiling all the code that uses it.
Guffa
A: 

I'm not sure if I'm misinterpreting things or not, but..

MyEnum.Red.ToString() should yield "Red" not 0. As such an Enum.Parse should be fine with a correct string evaluation. The problem you're describing would be int Foo = (int)MyEnum.Red; Which would be 0; This could cause the inconsistent results described on after a recompile with new elements.

Based on my understanding of the requirements, enum.ToString is persisted, there should be no issue. The issue is when persisting the value or the enum, not the string representation. Therefore, no hashing should be necessary.

Here's an example (pretend we added black)...

 class Program
 {
  static void Main(string[] args)
  {
   // this is the approach described
   string RedToString = MyEnum.Red.ToString();

   // We're pretending that red was the zeroth element when we did (int)MyEnum.Red;
   int RedToInt = 0;

   MyEnum RedFromString = (MyEnum)Enum.Parse(typeof(MyEnum), RedToString);
   MyEnum RedFromInt = (MyEnum)RedToInt;

   // this is in theory how RedToInt was persisted
   int BlackFromInt = (int)MyEnum.Black;

   Console.WriteLine("RedToString: " + RedToString); // Red
   Console.WriteLine("RedToInt: " + RedToInt); // 0
   Console.WriteLine("RedFromString: " + RedFromString); // Red
   Console.WriteLine("RedFromInt: " + RedFromInt); // Black
   Console.WriteLine("BlackFromInt: " + BlackFromInt); // 0
   Console.Read();
  }

  enum MyEnum
  {
   Black,
   Red,
   Blue,
  }
 }

HTH, Ben

Ben