gluk47
gluk47

Reputation: 1830

GetName for enum with duplicate values

If I have duplicate values in a C# enum, saying

enum MyE {
  value1 = 1,
  value2 = 2,
  valued = 1
}

What should be the values of the following strings?

MyE N = (MyE)1;
string V1 = N.ToString();
string V2 = GetName(MyE, 1);

Is it true that V1 and V2 must contain the same values? What these values should be?

I haven't found anything in MSDN or here concerning such a «dereferencing» of enums with duplicates, point me to a link, please, if I missed that.

Upvotes: 13

Views: 4735

Answers (5)

George
George

Reputation: 121

Whilst this might be a little late to the Party, this may not be the greatest idea, however, the one work around I did find in my case in which I've had to use duplicate enums is to use the following from the question here; Get String Name from Enum in C#

var name = nameof(DeclaredEnum.EnumName);

Resulting in:

EnumName

You can read more Here as to how it actually works.

Upvotes: 0

Phil1970
Phil1970

Reputation: 2624

Well, because there are no guarantees about the order and obsolete attribute seems to have no impact on Enum methods, I would usually suggest to use a string property for serialization purpose and use custom code to handle obsolete values.

That way, the primary Enum has no duplicate and contains only current values.

Depending on the serialization framework, you might be able to mark the string property as serializable but obsolete and the actual property used in code to be ignored by serialization.

To handle obsolete values in the string property setter, you can either use an [Obsolete] enum or a switch statement. Typically, I would probably use the former if I have a lot of obsolete values to handle and the latter if I have only a few values to handle.

Also it might make sense to put the code in an helper class or an extension method particularly if you load or store that value from a bunch of places.

Although I have not tried it much in production code, I am wondering if it would be better to use a struct instead of an enum to have more control... but I think it need a lot of boilerplate code that cannot easily be made generic.

Upvotes: 0

Sinatr
Sinatr

Reputation: 22008

I disagree with other answers statements

... this isn't guaranteed ...

... you cannot rely on that ...

as well as with msdn statement:

... your application code should never depend on the method returning a particular member's name ...

The story

There was an enum in my software

enum Blabla { A = 0, B = 1, C = 2, D = 3 }

at some point A value changes to AA and later AA changes to AAA. To keep backward compatibility I had to do

enum Blabla { A = 0, AA = 0, AAA = 0, B = 1, C = 2, D = 3 }

This allows to deserialize old enum value (made by older versions of software) as AAA.

Then there was a report which prints Blabla setting value. And at some point every customer using new version start telling me what instead of AAA they see AA value. All of them see AA (and no one report seeing A).

What I did? I simply change the order (until result was AAA)

enum Blabla { AAA = 0, A = 0, AA = 0, ...}

and made a test to ensure what Blabla.AAA will be output as AAA. Problem solved?

The proof

Looking at sources of Enum.ToString() (or Enum.GetName()), it uses GetEnumName(), which calls Array.BinarySearch() for sorted array of values to find an index of value.

The result of binary search is deterministic: providing it with the same parameters will return same result.

So:

  • if you don't change enum, then result will be the same.
  • it is possible to find result experimentally (or perhaps by understanding how binary search works and how enum will be processed).
  • there is no easy rule to determine result (e.g. you can't say "it always return first defined value").
  • it's unlikely what enum format will be changed (e.g. order of definition and order of list of values differs) or Enum.ToString() will be changed, it can happens however, so make sure you have tests for cases where you rely on return value.

Upvotes: 2

Ethan Brown
Ethan Brown

Reputation: 27292

From the "Remarks" section on the Enum.GetName method documentation (http://msdn.microsoft.com/en-us/library/system.enum.getname.aspx), it says:

If multiple enumeration members have the same underlying value, the GetName method guarantees that it will return the name of one of those enumeration members. However, it does not guarantee that it will always return the name of the same enumeration member. As a result, when multiple enumeration members have the same value, your application code should never depend on the method returning a particular member's name.

I ran a test to see what would happen experimentally, and it always returned the first value defined (in your example, value1), but according to the official documentation above, you cannot rely on that (see comment by @gluk47, indicating different behavior in the wild).

Upvotes: 2

ChrisF
ChrisF

Reputation: 137188

Experimentation shows that:

V1 = "value1"

and

V2 = "value1"

However, this isn't guaranteed. The MSDN page on Enum.GetName states:

If multiple enumeration members have the same underlying value, the GetName method guarantees that it will return the name of one of those enumeration members. However, it does not guarantee that it will always return the name of the same enumeration member. As a result, when multiple enumeration members have the same value, your application code should never depend on the method returning a particular member's name.

Upvotes: 18

Related Questions