Click here to Skip to main content
15,881,281 members
Articles / Programming Languages / C#

The Hidden Side-effect of Enums and Values

Rate me:
Please Sign up or sign in to vote.
3.55/5 (34 votes)
7 Feb 2019CPOL5 min read 20.4K   7   26
Recently, I encountered an issue with enums, which I wanted to share in case someone else encounters it along the way.

Introduction

Recently, I encountered an issue with enums, which I wanted to share in case someone else encounters it along the way.

Disclaimer: So that there is minimal miss-intepretations on this article, this is not a guide as how you should write enums, this is an article showing what the compiler allows to happen and it is meant as a way to spot the issue if you ever encounter it in code written by someone else or third party systems.

So what are enums?

Enums are a list of numerical constants that help us in a number of situations, like for example when something can have a tag or a property on it to distinguish it from another object, or they represent different options for methods. Other times, they represent states of an object or relationships like for example, we can have an Employee enum which tells us if a person is a manager, vice-president, CEO.

But no matter what name we give an enum, in the background, it is just a number used to represent that state like this:

C#
enum Foo {
Bar1,
Bar2,
Bar3,
}

So, in this case, we declared an enum called Foo which can have one of 3 values, Bar1, which implicitly has a value of 0, Bar2, which has a value of 1 and Bar3 which has a value of 2.

The issue lies in the background value of the enum and how we use it, for example since we know that Bar2 has a value of 1, we could cast the number 1 to an enum of type Foo and we will get Bar2 like this:

C#
Console.WriteLine((Foo)1);

// Will output: Bar2

But since we are talking about numbers, enums can also be mapped to a specific value like this:

C#
enum Foo {
Bar1 = 2,
Bar2,
Bar3 = 5,
}

Basically, in this case, Bar1 will have a value of 2, Bar2 will have a value of 3, and Bar3 will have a value of 5.

And now for the odd part and side-effect.

We can have an enum defined with two or more identifiers for the same value like so:

C#
enum Foo {
Bar1 = 2,
Bar2 = 2,
Bar3,
}

Notice that Bar1 and Bar2 have the same value (doesn’t have to be 2). So if we now run the following command, the run-time does not know to which identifier we are referring to so it will give the middle identifier with that value:

C#
Console.WriteLine((Foo)2);

// Will output: Bar2 because it is the latest

What I mean by the middle is that the output will be the same even if we have an enum defined like this:

C#
enum Foo {
Bar1 = 2,
Bar2 = 2,
Bar3 = 2,
}

So no matter how we run it, the output will still be Bar2 but if we have an enum defined like this:

C#
enum Foo {
Bar1 = 2,
Bar2 = 2,
Bar3 = 2,
Bar4 = 2,
Bar5 = 2,
Bar6 = 2,
Bar7 = 2
}

Running the same command will give us Bar4 because it’s the middle one, and if we have an even number of enums, it will give us the middle one closer to the end, so for two enum identifiers, it will give us the second one, for three it will give us the second one, but for four it will give us the third one, and again if we had five identifiers for the same value. For six, it will give us the third one, and so on and so forth.

But what happens when putting another enum identifier with a lower value before Bar1 like this?

C#
enum Foo {
Bar,
Bar1 = 2,
Bar2 = 2,
Bar3,
}

Now if we run the output command, it will not show the middle enum with that value, instead, it will show the middle value – 1 so in this case, it will show Bar1 and for the previous enum, but let us take it a step further and see for this one:

C#
enum Foo {
Bar,
Bar1 = 2,
Bar2 = 2,
Bar3 = 2,
Bar4 = 2,
Bar5 = 2,
Bar6 = 2,
Bar7 = 2
}

Then the output for the value 2 will be Bar3, and even worse, if we were to add 2 more values before Bar1 (I shifted the value to 5 so that we don’t overlap with the ones we’re trying to check:

C#
enum Foo {
Bar0,
Bar00,
Bar000,
Bar1 = 5,
Bar2 = 5,
Bar3 = 5,
Bar4 = 5,
Bar5 = 5,
Bar6 = 5,
Bar7 = 5
}

Then if we run the command:

C#
Console.WriteLine((Foo)5);

// Will output: Bar2

So for every 2 identifiers added before that sequence, it will go back one, but I tried something else and added 2 more identifiers after the sequence, and guess what, it went back to Bar3 and with another two, it went to Bar4 and if you keep going and adding so many identifiers that it should be more than Bar7, then it will cycle around and show Bar1.

This, I admit, baffled me a bit, because that means that using enums by value when we have more than one identifier for a given value it becomes unpredictable, especially when this isn’t clear and during the course of development, we add to that enum without knowing that it affects us and we might have outputs that depend on those identifiers.

Even though we won’t see all that many cases with more than two identifiers per value, it is still something to take note of because using enums by value is not that uncommon, and by that I mean that at least three common usages come to mind when I think of this, like HTML drop-downs which have number values behind them, WebAPI calls that use numbers to denote a certain enum value, databases persistence, like MongoDB will use the numerical value to store an enum, and I’m sure there are many more cases that use such mechanisms.

Fortunately, a colleague of mine came up with an answer to avoid this issue and that is to save or send enum values as text and then parse them, that way, we know for sure that we are referring to the right identifier.

I hope you found this as interesting and weird as I did, and if you know the reason why this happens, feel free to share and let me know because I admit, my curiosity has peaked.

Thank you and see you next time.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Romania Romania
When asked, I always see myself as a .Net Developer because of my affinity for the Microsoft platform, though I do pride myself by constantly learning new languages, paradigms, methodologies, and topics. I try to learn as much as I can from a wide breadth of topics from automation to mobile platforms, from gaming technologies to application security.

If there is one thing I wish to impart, that that is this "Always respect your craft, your tests and your QA"

Comments and Discussions

 
QuestionBad idea to give two (or more) times the same value Pin
Bertrand Gilliard11-Feb-19 5:18
Bertrand Gilliard11-Feb-19 5:18 
QuestionGood point - would not happen though Pin
Sammuel Miranda11-Feb-19 2:27
professionalSammuel Miranda11-Feb-19 2:27 
AnswerInsight as to how this might happen Pin
Vlad Neculai Vizitiu28-Jan-19 10:04
Vlad Neculai Vizitiu28-Jan-19 10:04 
News[My vote of 2] Mathematically determined. Pin
stixoffire22-Jan-19 2:15
stixoffire22-Jan-19 2:15 
GeneralRe: [My vote of 2] Mathematically determined. Pin
Heriberto Lugo9-Feb-19 20:44
Heriberto Lugo9-Feb-19 20:44 
GeneralMy vote of 1 Pin
Imirzyan18-Nov-18 19:43
Imirzyan18-Nov-18 19:43 
GeneralRe: My vote of 1 Pin
George Gomez7-Feb-19 12:25
George Gomez7-Feb-19 12:25 
GeneralRe: My vote of 1 Pin
Vlad Neculai Vizitiu7-Feb-19 12:34
Vlad Neculai Vizitiu7-Feb-19 12:34 
Suggestionmapping an enum to a string is implementation-dependant Pin
Philippe Verdy16-Nov-18 7:43
Philippe Verdy16-Nov-18 7:43 
GeneralRe: mapping an enum to a string is implementation-dependant Pin
Chad3F16-Nov-18 13:52
Chad3F16-Nov-18 13:52 
GeneralRe: mapping an enum to a string is implementation-dependant Pin
Philippe Verdy16-Nov-18 15:48
Philippe Verdy16-Nov-18 15:48 
PraiseNice Article Pin
MrFunke3.1416-Nov-18 4:06
MrFunke3.1416-Nov-18 4:06 
GeneralAn educational exercise Pin
SirGrowns15-Nov-18 21:19
professionalSirGrowns15-Nov-18 21:19 
GeneralMy vote of 5 Pin
Donmorcombe15-Nov-18 14:22
Donmorcombe15-Nov-18 14:22 
GeneralSide effect of ToString Pin
Member 812807315-Nov-18 12:20
Member 812807315-Nov-18 12:20 
QuestionDoes it matter Pin
TrendyTim15-Nov-18 12:11
TrendyTim15-Nov-18 12:11 
GeneralMy vote of 5 Pin
dmjm-h15-Nov-18 11:35
dmjm-h15-Nov-18 11:35 
Interesting article and well-written.
GeneralI would consider an enum like you're proposing to be a 'code smell' Pin
Will Wayne15-Nov-18 11:09
Will Wayne15-Nov-18 11:09 
GeneralRe: I would consider an enum like you're proposing to be a 'code smell' Pin
Philippe Verdy17-Nov-18 6:21
Philippe Verdy17-Nov-18 6:21 
QuestionAnswer in the form of a question... Pin
Member 460345715-Nov-18 10:41
Member 460345715-Nov-18 10:41 
SuggestionNo dupes? Pin
vbjay.net15-Nov-18 9:28
vbjay.net15-Nov-18 9:28 
GeneralRe: No dupes? Pin
Member 1372366918-Nov-18 22:02
Member 1372366918-Nov-18 22:02 
GeneralRe: No dupes? Pin
Heriberto Lugo9-Feb-19 20:46
Heriberto Lugo9-Feb-19 20:46 
GeneralRe: No dupes? Pin
Philippe Verdy1-Mar-19 9:57
Philippe Verdy1-Mar-19 9:57 
QuestionThat is bizarre Pin
Marc Clifton15-Nov-18 0:28
mvaMarc Clifton15-Nov-18 0:28 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.