Diving Into OOP (Day 6) : Understanding Enums in C# (A Practical Approach)

Introduction

This article of the series “Diving into OOP” will explain the enum datatype in C#. We'll learn by doing a hands-on lab, not just by theory. We'll explore the power of enum and will cover almost every scenario in which we can use an enum. We'll follow a practical approach of learning to understand this concept. We may come across complex examples to understand the concept more deeply.

Enums (The definition)


Let's start with the definition taken from the MSDN

“The enum keyword is used to declare an enumeration, a distinct type that consists of a set of named constants called the enumerator list.

Usually it is best to define an enum directly within a namespace so that all classes in the namespace can access it with equal convenience. However, an enum can also be nested within a class or struct.

By default, the first enumerator has the value 0 and the value of each successive enumerator is increased by 1. For example, in the following enumeration, Sat is 0, Sun is 1, Mon is 2 and so forth.”



Image credit: http://pixabay.com/en/job-interview-career-conference-156130/

Pre-requisites:

I expect that my readers of this article should have a very basic knowledge of C# and I always wish that my readers should enjoy reading this article. Also keep writing programs by yourself that are given as examples in this article, to get the hands-on and understand the concept more deeply.

Let's recall our road map as in the following:



Image credit: http://borgefagerli.com/at-a-crossroads/cross-roads/

 

A Practical Approach

Enum plays almost the same responsibility as the class does, in other words creating a new data type and it exists at the same level as class, interfaces or structs.

Just open your Visual Studio and add a console application named Enums. You'll get a Program class.

Note: Each and every code snippet in this article is tried and tested.

Declare an enum at the same level as of the Program class, call it Color.

Program.cs

  1. namespace Enums  
  2. {  
  3.     class Program  
  4.     {  
  5.         static void Main(string[] args)  
  6.         {  
  7.         }  
  8.     }  
  9.   
  10.     enum Color  
  11.     {  
  12.         Yellow,  
  13.         Blue,  
  14.         Brown,  
  15.         Green  
  16.   
  17.     }  

In the preceding example, we created a new datatype using an enum. The datatype is Color having four distinct values Yellow, Blue, Brown and Green. The text that we write inside the declared enum could be anything of your wish; it just provides a custom enumerated list to you.

Modify your main program as shown below.

  1. using System;  
  2. namespace Enums  
  3. {  
  4.     class Program  
  5.     {  
  6.         static void Main(string[] args)  
  7.         {  
  8.             Console.WriteLine(Color.Yellow);  
  9.             Console.ReadLine();  
  10.         }  
  11.     }  
  12.   
  13.     enum Color  
  14.     {  
  15.         Yellow,  
  16.         Blue,  
  17.         Brown,  
  18.         Green  
  19.   
  20.     }  

Run the program.

Output: Yellow

Now if we just typecast Color.Yellow to int, what do we get?

  1. using System;  
  2. namespace Enums  
  3. {  
  4.     class Program  
  5.     {  
  6.         static void Main(string[] args)  
  7.         {  
  8.             Console.WriteLine((int)Color.Yellow);  
  9.             Console.ReadLine();  
  10.         }  
  11.     }  
  12.   
  13.     enum Color  
  14.     {  
  15.         Yellow,  
  16.         Blue,  
  17.         Brown,  
  18.         Green  
  19.   
  20.     }  

Output: 0

We see that enums are called static variables, so an enum can be considered here as static objects. Therefore other enums in the preceding example can be declared in the same way as Yellow, like Blue can be declared as Color.Blue. The output in the above two examples we see is 0 when we type cast and Yellow without typecasting, hence we see here that its behaviour is very similar to an array where Yellow has a value 0, similarly Blue has a value 1, Brown: 2, Green:3.

Therefore when we do Color.Yellow, it's like displaying a number 0, so from this we can infer that an enum represents a constant number, therefore an enum type is a distinct type having named constants.

A point to remember: An enum represents a constant number and an enum type is known as a distinct type having named constants.

Underlying data type

Program.cs

  1. using System;  
  2. namespace Enums  
  3. {  
  4.     class Program  
  5.     {  
  6.         static void Main(string[] args)  
  7.         {  
  8.             Console.WriteLine((byte)Color.Yellow);  
  9.             Console.WriteLine((byte)Color.Blue);  
  10.             Console.ReadLine();  
  11.         }  
  12.     }  
  13.   
  14.     enum Color:byte  
  15.     {  
  16.         Yellow,  
  17.         Blue,  
  18.         Brown,  
  19.         Green  
  20.   
  21.     }  

Output

0
1

Note: Each and every code snippet in this article is tried and tested.

The only change we did here is that we specified the type to the underlying enum that we declared. The default datatype for the enum is int, here we have specified the data type as byte and we get the result.

There are more data types that can be specified for enum like long, ulong, short, ushort, int, uint, byte and sbyte.

Point to remember: We can't declare char as an underlying data type for enum objects because char stores Unicode characters, but enum objects data type can only be a number.

Inheritance in enum

Program.cs

  1. using System;  
  2. namespace Enums  
  3. {  
  4.     class Program  
  5.     {  
  6.         static void Main(string[] args)  
  7.         {  
  8.             Console.WriteLine((byte)Color.Yellow);  
  9.             Console.WriteLine((byte)Color.Blue);  
  10.             Console.ReadLine();  
  11.         }  
  12.     }  
  13.   
  14.     enum Color:byte  
  15.     {  
  16.         Yellow,  
  17.         Blue,  
  18.         Brown,  
  19.         Green  
  20.   
  21.     }  
  22.   
  23.     enum Shades:Color  
  24.     {  
  25.           
  26.     }  

Output:

Compile time error: Type byte, sbyte, short, ushort, int, uint, long, or ulong expected

We clearly see here enums can't be derived from any other type except that specified in the error.

Point to remember: an enum can't be derived from any other type except that of type byte, sbyte, short, ushort, int, uint, long, or ulong

Let's derive a class from an enum, call it class Derived, so our code is as in the following.

Program.cs:

  1. class Program  
  2.     {  
  3.         static void Main(string[] args)  
  4.         {  
  5.             Console.WriteLine((byte)Color.Yellow);  
  6.             Console.WriteLine((byte)Color.Blue);  
  7.             Console.ReadLine();  
  8.         }  
  9.     } 

Enum:

  1. enum Color:byte  
  2. {  
  3.     Yellow,  
  4.     Blue,  
  5.     Brown,  
  6.     Green  
  7.   

Derived.cs:

  1. class Derived:Color  
  2.     {  
  3.           
  4.     } 

Compile the code.

Output:

Compile time error: 'Enums.Derived': cannot derive from sealed type 'Enums.Color'



Image credit: https://www.flickr.com/photos/lwr/931211869/

So, the point to remember is: By default an enum is a sealed class and therefore conforms to all the rules that a sealed class follows, so no class can derive from an enum, in other words a sealed type.

Can System.Enum be a base class to enum?

Program.cs:

  1. using System;  
  2.   
  3. namespace Enums  
  4. {  
  5.     internal enum Color: System.Enum  
  6.     {  
  7.         Yellow,  
  8.         Blue  
  9.     }  
  10.   
  11.     internal class Program  
  12.     {  
  13.         private static void Main(string[] args)  
  14.         {  
  15.         }  
  16.     }  

Output:

Compile time error: Type byte, sbyte, short, ushort, int, uint, long, or ulong expected

Point to remember: The enum type is implicitly derived from System.Enum and so we cannot explicitly derive it from System.Enum.

To add more, an enum is also derived from the three interfaces IComparable, IFormattable and IConvertible.

A. IComparable

Let's check.

Program.cs:

  1. using System;  
  2.   
  3. namespace Enums  
  4. {  
  5.     internal enum Color  
  6.     {  
  7.         Yellow,  
  8.         Blue,  
  9.         Green  
  10.     }  
  11.   
  12.     internal class Program  
  13.     {  
  14.         private static void Main(string[] args)  
  15.         {  
  16.             Console.WriteLine(Color.Yellow.CompareTo(Color.Blue));  
  17.             Console.WriteLine(Color.Blue.CompareTo(Color.Green));  
  18.             Console.WriteLine(Color.Blue.CompareTo(Color.Yellow));  
  19.             Console.WriteLine(Color.Green.CompareTo(Color.Green));  
  20.             Console.ReadLine();  
  21.         }  
  22.     }  

Output:

-1
-1
1
0

Sometimes we may get into situations where we have a large number of enums defined and we want to compare the values of the enum to each other to check if they are smaller, larger or an equal value to one another.

Since all enums implicitly derive from an Enum class that implements the interface IComparable, so they all have a method CompareTo(), that we just use in the above example. The method being non-static must be used through a member. Yellow has the value 0, Blue has 1 and Green has 2. In the first statement, when Color.Yellow is comared to Color.Blue, the value of Yellow is less than Blue hence -1 is returned. The same applies for the second statement when Color.Blue is compared to Color.Green. Green has a larger value, in other words 2 than that of Color.Blue having value 1 only. In the third statement, in other words the revers of the first statement, we get the result of comparison as 1, because Blue is greater than Yellow. In the last statement where Color.Green compares to itself, we undoubtedly get the value 0.

So the value -1 means the value is smaller, 1 means the value is larger and 0 means equal values for both the enum members.

The following is another comparison example.

Program.cs:

  1. using System;  
  2.   
  3. namespace Enums  
  4. {  
  5.      enum Color  
  6.     {  
  7.         Yellow,  
  8.         Blue,  
  9.         Green  
  10.     }  
  11.   
  12.     internal class Program  
  13.     {  
  14.         private static void Main(string[] args)  
  15.         {  
  16.             int myColor = 2;  
  17.             if(myColor== Color.Green)  
  18.             {  
  19.                 Console.WriteLine("my color");  
  20.             }  
  21.            Console.ReadLine();  
  22.         }  
  23.     }  

Output:

Compile time error : Operator '==' cannot be applied to operands of type 'int' and 'Enums.Color'

In the above example we tried to compare an int type to a Enum type and that resulted in a compile time error. Since an enum acts as an individual data type so it cannot be directly compared to an int, however, we can typecast the enum type to an int to do the comparison, like in the following example.

Program.cs:

  1. using System;  
  2.   
  3. namespace Enums  
  4. {  
  5.      enum Color  
  6.     {  
  7.         Yellow,  
  8.         Blue,  
  9.         Green  
  10.     }  
  11.   
  12.     internal class Program  
  13.     {  
  14.         private static void Main(string[] args)  
  15.         {  
  16.             int myColor = 2;  
  17.             if(myColor== (int)Color.Green)  
  18.             {  
  19.                 Console.WriteLine("my color");  
  20.             }  
  21.            Console.ReadLine();  
  22.         }  
  23.     }  

Output: my color

B. IFormattable


Program.cs:

  1. using System;  
  2.   
  3. namespace Enums  
  4. {  
  5.     internal enum Color  
  6.     {  
  7.         Yellow,  
  8.         Blue,  
  9.         Green  
  10.     }  
  11.   
  12.     internal class Program  
  13.     {  
  14.         private static void Main(string[] args)  
  15.         {  
  16.             System.Console.WriteLine(Color.Format(typeof(Color), Color.Green, "X"));  
  17.             System.Console.WriteLine(Color.Format(typeof(Color), Color.Green, "d"));  
  18.             Console.ReadLine();  
  19.         }  
  20.     }  

Output:

00000002
2

Format is the method derived from the IFormatter interface. It's a static method so can be used directly with the enum class defined as Color. It's first parameter is the type of the enum class, the second is the member that must be formatted and the third is the format, in other words hexadecimal or decimal, like we used in the above example and we got a positive result output too.

C. IConvertible

  1. using System;  
  2.   
  3. namespace Enums  
  4. {  
  5.      enum Color  
  6.     {  
  7.         Yellow,  
  8.         Blue,  
  9.         Green  
  10.     }  
  11.   
  12.     internal class Program  
  13.     {  
  14.         private static void Main(string[] args)  
  15.         {  
  16.             string[] names;  
  17.             names = Color.GetNames(typeof (Color));  
  18.             foreach (var name in names)  
  19.             {  
  20.                 Console.WriteLine(name);  
  21.             }  
  22.             Console.ReadLine();  
  23.         }  
  24.     }  

Output:

Yellow
Blue
Green

Note: Each and every code snippet in this article is tried and tested.

GetNames is a static method that accepts Type, in other words an instance of type as a parameter and in return gives an array of strings. Like in the above example, we had an array of 3 members in our enum, therefore thier names are displayed one by one.

The following is another example.

Program.cs

  1. using System;  
  2.   
  3. namespace Enums  
  4. {  
  5.      enum Color  
  6.     {  
  7.         Yellow,  
  8.         Blue,  
  9.         Green  
  10.     }  
  11.   
  12.     internal class Program  
  13.     {  
  14.         private static void Main(string[] args)  
  15.         {  
  16.            Console.WriteLine(Color.Blue.ToString());  
  17.            Console.WriteLine(Color.Green.ToString());  
  18.            Console.ReadLine();  
  19.         }  
  20.     }  

Output:

Blue
Green

As we see in the above example, we converted an enum type to a starting type and got an output too, so, numerous predefined conversion methods can be used to convert an enum from one data type to another.

Point to remember: numerous predefined conversion methods can be used to convert an enum from one data type to another.

Duplicity, default values and initialization

Program.cs:

  1. using System;  
  2. namespace Enums  
  3. {  
  4.     class Program  
  5.     {  
  6.         static void Main(string[] args)  
  7.         {  
  8.             Console.WriteLine((byte)Color.Yellow);  
  9.             Console.WriteLine((byte)Color.Blue);  
  10.             Console.ReadLine();  
  11.         }  
  12.     }  
  13.   
  14.     enum Color  
  15.     {  
  16.         Yellow,  
  17.         Blue,  
  18.         Brown,  
  19.         Green,  
  20.         Blue  
  21.   
  22.     }  

Output:

Compile time error: The type 'Enums.Color' already contains a definition for 'Blue'

In the preceding example we just repeated the enum member Blue of Color and we got a compile time error, hence we now know that an enum cannot contain two members having the same name. By default if the first value is not specified then the first member takes the value 0 and increments it by one for succeeding members.

Let's use one more example.

Program.cs:

  1. using System;  
  2. namespace Enums  
  3. {  
  4.     class Program  
  5.     {  
  6.         static void Main(string[] args)  
  7.         {  
  8.             Console.WriteLine((int)Color.Yellow);  
  9.             Console.WriteLine((int)Color.Blue);  
  10.             Console.WriteLine((int)Color.Brown);  
  11.             Console.WriteLine((int)Color.Green);  
  12.   
  13.             Console.ReadLine();  
  14.         }  
  15.     }  
  16.   
  17.     enum Color  
  18.     {  
  19.         Yellow =2,  
  20.         Blue,  
  21.         Brown=9,  
  22.         Green,  
  23.   
  24.     }  

Output:

2
3
9
10

Surprise! We can always specify the default constant value to any enum member, here we see, we specified the value 2 for yellow, so as per the law of enum, the value of blue will be incremented by one and gets the value 3. We again specified 9 as a default value to Brown and so its successor Green gets incremented by one and gets that value 10.

Moving on to another example.

Program.cs:

  1. using System;  
  2. namespace Enums  
  3. {  
  4.     class Program  
  5.     {  
  6.         static void Main(string[] args)  
  7.         {  
  8.              
  9.         }  
  10.     }  
  11.   
  12.     enum Color:byte  
  13.     {  
  14.         Yellow =300 ,  
  15.         Blue,  
  16.         Brown=9,  
  17.         Green,  
  18.   
  19.     }  

Output:

Compile time error: Constant value '300' cannot be converted to a 'byte'

We just derived our enum from byte, we know we can do that . We then change the value of Yellow from 2 to 300 and we resulted in a compile time error. Since here our underlying data type was byte, it is as simple as that, that we cannot specify the value to enum members that exceeds the range of underlying data types. The value 300 is beyond the range of byte. It is similar to assigning the beyond range value to a byte data type variable.

The following is another example.

Program.cs:

  1. using System;  
  2. namespace Enums  
  3. {  
  4.     class Program  
  5.     {  
  6.         static void Main(string[] args)  
  7.         {  
  8.             Console.WriteLine((int)Color.Yellow);  
  9.             Console.WriteLine((int)Color.Blue);  
  10.             Console.WriteLine((int)Color.Brown);  
  11.             Console.WriteLine((int)Color.Green);  
  12.   
  13.             Console.ReadLine();  
  14.         }  
  15.     }  
  16.   
  17.     enum Color  
  18.     {  
  19.         Yellow = 2,  
  20.         Blue,  
  21.         Brown = 9,  
  22.         Green = Yellow  
  23.     }  

Output:

2
3
9
2

Here we initialized Green to Yellow and we did not get an error, so we see, more than one enum members can be initialized with the same constant value.

Point to remember: More than one enum members can be initialized with the same constant value.

Program.cs:

  1. using System;  
  2. namespace Enums  
  3. {  
  4.     class Program  
  5.     {  
  6.         static void Main(string[] args)  
  7.         {  
  8.             Color.Yellow = 3;  
  9.         }  
  10.     }  
  11.   
  12.     enum Color  
  13.     {  
  14.         Yellow = 2,  
  15.         Blue,  
  16.         Brown = 9,  
  17.         Green = Yellow  
  18.     }  

Output:

Compile time error: The left-hand side of an assignment must be a variable, property or indexer

In the preceding example, we tried to initialize the enum member out of the scope of the defined enum, in other words in another class and got a compile time error. We must not forget that an enum acts as a constant, that cannot change its value.

Point to remember: An enum acts as a constant, so its value cannot be changed once initialized.

Readability

Program.cs:

  1. using System;  
  2.   
  3. namespace Enums  
  4. {  
  5.     internal enum Color  
  6.     {  
  7.         Yellow,  
  8.         Blue,  
  9.         Brown,  
  10.         Green  
  11.     }  
  12.   
  13.     internal class Program  
  14.     {  
  15.         private static void Main(string[] args)  
  16.         {  
  17.             Console.WriteLine(CheckColor(Color.Yellow));  
  18.             Console.WriteLine(CheckColor(Color.Brown));  
  19.             Console.WriteLine(CheckColor(Color.Green));  
  20.             Console.ReadLine();  
  21.         }  
  22.   
  23.         public static string CheckColor(Color color)  
  24.         {  
  25.             switch (color)  
  26.             {  
  27.                 case Color.Yellow:  
  28.                     return "Yellow";  
  29.                 case Color.Blue:  
  30.                     return "Blue";  
  31.                 case Color.Brown:  
  32.                     return "Brown";  
  33.                 case Color.Green:  
  34.                     return "Green";  
  35.                 default:  
  36.                     return "no color";  
  37.   
  38.             }  
  39.         }  
  40.     }  

Output:

Yellow
Brown
Green

Here, in the above example we declared an enum Color containing various color members.There is a class named program that contains a static method named CheckColor, that has a switch statement checking the color on the basis of the ed parameter to the method, in other words an Enum Color. In the Main method we try to access that CheckColor method, ing various parameters. We see that the the switch statement in the CheckColor method can use any of the datatypes ed and in return the case statements use the name of that type and not the plain int number to compare the result. We see that this made our program more readable. So an enum plays an important role in making the program more readable, structured and easy to grasp.

Circular dependency

Program.cs:

  1. using System;  
  2.   
  3. namespace Enums  
  4. {  
  5.     internal enum Color  
  6.     {  
  7.         Yellow=Blue,  
  8.         Blue  
  9.     }  
  10.   
  11.     internal class Program  
  12.     {  
  13.         private static void Main(string[] args)  
  14.         {  
  15.         }  
  16.     }  

Output:

Compile time error: The evaluation of the constant value for 'Enums.Color.Yellow' involves a circular definition

Like constants we also cannot have circular dependency in enums. We assigned the value Blue to Yellow and Blue in turn is incremented by one as a next enum member, this results in a circular dependency of Blue to yellow and resulted in an error, C# is smart enough to catch these kinds of tricks.

Diving Deep

Let's take some complex scenarios.

Lab 1

Program.cs:

  1. using System;  
  2.   
  3. namespace Enums  
  4. {  
  5.      enum Color  
  6.     {  
  7.         
  8.     }  
  9.   
  10.     internal class Program  
  11.     {  
  12.         private static void Main(string[] args)  
  13.         {  
  14.             Color color = (Color) -1;  
  15.            Console.ReadLine();  
  16.         }  
  17.     }  

Note: Each and every code snippet in this article is tried and tested.

Output:

Compile time error:
To cast a negative value, you must enclose the value in parentheses
'Enums.Color' is a 'type' but is used like a 'variable'


In the preceding example, we are casting a negative value to enum, but the compiler says that while casting a negative value, we must keep that in parenthesis. It's not strange, since C# knows that “-” is also a unary operator, that use of the code above may create confusion for the compiler of whether we are using subtraction or we are typecasting a negative value. So always use parenthesis while typecasting negative values.

Lab 2



Program.cs:

  1. using System;  
  2.   
  3. namespace Enums  
  4. {  
  5.      enum Color  
  6.     {  
  7.       value__  
  8.     }  
  9.   
  10.     internal class Program  
  11.     {  
  12.         private static void Main(string[] args)  
  13.         {  
  14.               
  15.         }  
  16.     }  

Output:

Compile time error: The enumerator name 'value__' is reserved and cannot be used

We clearly see here that we have value__ as a reserved member for the enumerator. The C# compiler like this keyword has a large number of reserved builtin keywords.



Image credit: www.vector.rs

It may keep this reserved keyword to keep track of the enum members internally but not sure.

Summary:

Let's recall all the points that we need to remember.


  1. An enum represents for a constant number and an enum type is known as a distinct type having named constants.
  2. We can't declare char as an underlying data type for enum objects because char stores Unicode characters, but enum objects data type can only be number.
  3. An enum can't be derived from any other type except that of type byte, sbyte, short, ushort, int, uint, long, or ulong.
  4. By default an enum is a sealed class and therefore conforms to all the rules that a sealed class follows, so no class can derive from an enum, in other words a sealed type.
  5. The enum type is implicitly derived from System.Enum and so we cannot explicitly derive it from System.Enum.
  6. An enum is also derived from the three interfaces IComparable, IFormattable and IConvertible.
  7. A numerous predefined conversion methods can be used to convert an enum from one data type to another.
  8. More than one enum member can be initialized a same constant value.
  9. An enum acts as a constant, so its value cannot be changed once initialized.
  10. The enumerator name "value__" is reserved and cannot be used.

Conclusion

With this article we completed nearly all the scenarios related to enum. We did many of hands-on labs to clarify our concepts. I hope my readers now know by heart these basic concepts and will never forget them.

These may also help you in cracking C# interviews.

Keep coding and enjoy reading.

Also do not forget to rate/comment/like my article if it helped you by any means, this helps me to get motivated and encourages me to write more and more.

Read more

For more technical articles you can reach out to CodeTeddy

My other series of articles,

Happy coding !


Similar Articles