Chapter 3

 

Enums

 

An enum is at the same level as a class, struct and interface. It performs a similar job of creating a new data type.

 

a.cs

class zzz

{

public static void Main()

{

}

}

enum yyy

{

a1,black,hell

}

 

We have created a new datatype yyy using enum. It has three values, namely, a1, black and hell. The words we write within an enum can be anything, it is left entirely on us.

 

a.cs

class zzz

{

public static void Main()

{

System.Console.WriteLine((int)yyy.black);

}

}

enum yyy

{

a1,black,hell

}

 

Output

1

 

An enum can be considered as a static object, hence black can be referred to as yyy.black. In the WriteLine method, when we cast yyy.black to int, the output returned is an integer value of 1. Without any cast, the output is displayed as black.

 

In a sense, it behaves like an array where a1 has a value of zero, black has a value of 1 and hell 2. Wherever we refer to yyy.black, it is like writing the number 1. At one level an enum is a word that stands for a constant number. An enum type is called a distinct type with named constants.

 

a.cs

class zzz {

public static void Main()

{

System.Console.WriteLine((byte)yyy.black);

}

}

enum yyy : byte

{

a1,black,hell

}

 

 

Output

1

 

In the earlier example on enum, the data type for the members was int as we did not specify the underlying data type. Here we are specifying byte as the underlying data type.

The valid underlying types are byte, sbyte, short, ushort, int, uint, long or ulong respectively. A char cannot be the underlying type for an enum data object as a char stores Unicode characters and not numbers.

 

a.cs

class zzz

{

public static void Main()

{

}

}

enum yyy

{

a1,black,hell

}

enum xxx : yyy

{

}

 

Compiler Error

a.cs(12,1): error CS1008: Type byte, sbyte, short, ushort, int, uint, long, or ulong expected

 

Enums cannot derive from other enums and can only have the above mentioned types as the underlying data types.

 

a.cs

class zzz

{

public static void Main()

{

}

}

enum yyy

{

a1,black,hell

}

class xxx : yyy

{

}

 

Compiler Error

a.cs(11,7): error CS0509: 'xxx' : cannot inherit from sealed class 'yyy'

 

Internally, an enum is treated as a sealed class and thus follows all the rules pertaining to sealed classes.

 

a.cs

class zzz

{

}

enum yyy

{

a1,black,hell,a1

}

 

Compiler Error

a.cs(6,15): error CS0102: The class 'yyy' already contains a definition for 'a1'

 

An enum cannot have two members with the same name. Every enum member has a value which if not specified starts with 0 and the next enum member increments it by 1.

 

a.cs

class zzz

{

public static void Main()

{

System.Console.WriteLine((int)yyy.a1 + " " + (int)yyy.black + " " + (int)yyy.hell + " " + (int)yyy.a2);

}

}

enum yyy

{

a1 = 4,black,hell = 9,a2

}

 

Output

4 5 9 10

 

We can, however, specify the constant values for the enum members. a1 starts at 4 and as we have not specified a value for black, it is 5. Hell, though, starts at 9 and a2, the next member, increases it by one thus getting a value of 10.

 

a.cs

class zzz

{

public static void Main()

{

}

}

enum yyy : byte

{

a1 = 400,black,hell = 9,a2

}

 

Compiler Error

a.cs(9,6): error CS0031: Constant value '400' cannot be converted to a 'byte'

 

If we try and go beyond the range of values supported by the underlying data type, we receive an error similar to the one we'd get if we initialize a byte to a value beyond its capacity.

 

a.cs

class zzz

{

public static void Main()

{

System.Console.WriteLine((int)yyy.a1 + " " + (int)yyy.black + " " + (int)yyy.hell + " " + (int)yyy.a2);

}

}

enum yyy

{

a1 = 4,black,hell = 9,a2=a1

}

 

Output

4 5 9 4

Nothing stops us from initializing more than one enum member to the same constant value.

 

a.cs

class zzz

{

public static void Main()

{

yyy.a1 = 10;

}

}

enum yyy

{

a1 = 4,black,hell = 9,a2=a1

}

 

Compiler Error

a.cs(5,1): error CS0131: The left-hand side of an assignment must be a variable, property or indexer

a.cs(5,10): error CS0029: Cannot implicitly convert type 'int' to 'yyy'

 

An enum member behaves like a constant whose value cannot be changed.

 

a.cs

using System;

enum yyy

{

red,green,blue = 3

}

class zzz

{

public static void Main() {

Console.WriteLine(xxx(yyy.blue));

Console.WriteLine(xxx(yyy.green));

}

static string xxx(yyy c) {

switch (c) {

            case yyy.red:

            return "red";

            case yyy.blue:

            return "blue";

            case yyy.green:

            return "green";

            default:

            return "invalid";

                        }

            }

}

 

Output

blue

green

 

Here we have created an enum that has three members. The switch statement can take any data type and the case statement uses the member names rather than numbers to compare for a match. This makes our program more readable and easier to document.

 

a.cs

enum yyy {

red = green ,green

}

class zzz

{

public static void Main()

{

}

}

 

Compiler Error

a.cs(3,1): error CS0110: The evaluation of the constant value for 'yyy.red' involves a circular definition

 

Similar to constants, we cannot have a circular definition. Here red depends on green and the value of green is one more than the value of red. This circular definition causes an error.

 

An enum refers to its own members without a qualifier whereas any external identifier  must precede it  with the name of the enum. By default, an enum and its members are public. We can explicitly use the public modifier if we so choose to. The same accessibility rules of classes apply here also.

a.cs

class zzz

{

public static void Main()

{

}

}

enum yyy : System.Enum

{

red ,green

}

 

Compiler Error

a.cs(8,1): error CS1008: Type byte, sbyte, short, ushort, int, uint, long, or ulong expected

 

Every enum data type is automatically derived from System.Enum. However, we cannot derive from it explicitly. The enum class has also been derived from three interfaces, IComparable, IFormattable and IConvertible.

 

a.cs

class zzz {

public static void Main()

{

System.Console.WriteLine(yyy.green.CompareTo(yyy.blue));

System.Console.WriteLine(yyy.green.CompareTo(yyy.red));

System.Console.WriteLine(yyy.green.CompareTo(yyy.green));

}

}

enum yyy

{

red,green,blue

}

 

Output

-1

1

0

 

Many a times we would like to compare between enums as we do with strings. We may want to know whether an enum member holds a value larger, equal to or smaller than another enum member. As all enums finally derive from the Enum class, they have a function called CompareTo. This function is not static so we have to execute it through a member. The member red has a value 0, green 1 and blue 2. Thus yyy.green is smaller than blue but larger than red. A negative answer is smaller and a positive value other than zero is larger.

 

a.cs

class zzz {

public static void Main()

{

System.Console.WriteLine(yyy.Format(typeof(yyy),yyy.green,"X"));

System.Console.WriteLine(yyy.Format(typeof(yyy),yyy.green,"d"));

}

}

enum yyy

{

red,green,blue

}

 

Output

00000001

1

 

Format is a freely available static function with every enumeration. Hence we can access it through the yyy class. The first parameter is the data type of  enum, the second the value of the enum member and finally a string specifying the format i.e. hex or decimal.

 

a.cs

class zzz

{

public static void Main()

{

string [] a;

a = yyy.GetNames(typeof(yyy));

foreach ( string s in a)

System.Console.WriteLine(s);

}

}

enum yyy

{

red,green,blue

}

 

Output

red

green

blue

 

The static function GetNames accepts an object of type Type and it returns an array of strings. In our case, the array has the length of 3 as yyy has 3 members. The foreach statement is used to displays them one by one.

 

a.cs

class zzz

{

public static void Main()

{

System.Console.WriteLine(yyy.green.ToString());

}

}

enum yyy

{

red,green,blue

}

 

Output

green

 

We can use a large number of pre-defined conversion functions with enums to convert from one data type to another.

 

a.cs

public enum yyy

{

red ,green

}

class zzz

{

public static void Main()

{

int i = 1;

if ( i == yyy.red)

;

}

}

 

Compiler Error

a.cs(10,6): error CS0019: Operator '==' cannot be applied to operands of type 'int' and 'yyy'

 

An enum is a distinct data type and it cannot be equated to an int. We need to overload the operator to perform comparisions  between them.

 

a.cs

public enum yyy

{

red ,green

}

class zzz

{

public static void Main()

{

int i = 1;

if ( i == (int)yyy.green)

System.Console.WriteLine("hi");

}

}

 

 

Output

hi

 

The only way to remove the earlier error is by casting yyy.green to int.

 

a.cs

public enum aa : int

{

a1,a2,a3

}

class zzz

{

public static void Main()

{

System.Console.WriteLine(aa.a2 + 10);

System.Console.WriteLine(10 + aa.a3);

}

}

 

Output

11

12

 

The + operator is internally overloaded to add a number to an enum. There are actually two such operators, the order of priority does not matter. These operators are available each time we create an enum. We now have a choice of using the earlier members of the enum class or the free operators.

 

a.cs

public enum aa : int

{

a1,a2,a3

}

class zzz

{

public static void Main()

{

System.Console.WriteLine(aa.a2 + aa.a3);

}

}

 

Compiler Error

a.cs(9,26): error CS0019: Operator '+' cannot be applied to operands of type 'aa' and 'aa'

 

However, we cannot add two enums members even though they belong to the same enum family. Individually they are know to be numbers and can be added up with another number separately.

 

 

a.cs

class zzz

{

public static void Main()

{

System.Console.WriteLine(yyy.a1 == yyy.a2);

System.Console.WriteLine(yyy.a1 == yyy.a3);

}

}

enum yyy

{

a1 = 1,a2 = 4,a3=1

}

 

Output

False

True

 

The comparison operators ==, != etc. get automatically overloaded to work with the user defined enums. As mentioned earlier, enums are glorified numbers.

 

a.cs

class zzz

{

public static void Main()

{

System.Console.WriteLine(yyy.a1 == xxx.a1);

}

}

enum yyy

{

a1 = 1,a2 = 4,a3=1

}

enum xxx

{

a1 = 1,a2 = 4,a3=1

}

 

Compiler Error

a.cs(5,26): error CS0019: Operator '==' cannot be applied to operands of type 'yyy' and 'xxx'

The above error is a direct proof stating that enums may be glorified numbers but cannot be used to compare enums members from different families. All the other comparison operators work in exactly the same way.

 

C# gives the same weight to the enum line of data types as provided to classes. Thus it goes to great extents to make life easier for us when working with the enum data types.

 

The Other Odds and Ends

 

a.cs

enum yyy

{

}

class zzz

{

public static void Main()

{

yyy i = (yyy) -1 ;

}

}

 

Compiler Error

a.cs(8,10): error CS0118: 'yyy' denotes a 'class' where a 'variable' was expected

a.cs(8,9): error CS0075: To cast a negative value, you must enclose the value in parentheses

 

To cast a -ve value, it must be placed in parenthesis. This may be because of the fact that the unary minus is also an operator and may confuse the compiler. In doubt, always use parenthesis. An apple a day keeps the doctor away and an extra parenthesis has done no one any harm.

 

a.cs

enum eee

{

value__

}

 

Compiler Error

a.cs(3,1): error CS0076: The enumerator name 'value__' is reserved and cannot be used

 

The compiler has a large number of reserved words. It converts the code to a different form altogether. Only for an enum does it not allow us to use the reserved word value__ as it must be using the same word internally to keep track of the enum. That is what we think but the right answer is only with the people who designed the language. The long and short is that we are not allowed to use certain words in our code at certain places. It is like the charge of the light brigade. Ours is not to reason why, ours is but to do and die.

 

a.cs

enum eee : byte

{

a = 255,b

} 

 

Compiler Error

a.cs(3,9): error CS0543: 'eee.b': the enumerator value is too large to fit in its type

 

A byte can only store values from 0 to 255. In the case of an enum, the value of enum member a is 255 and that of b is 256. However, a byte cannot store such a large number. Thus the error is reported at b and not at a. Also if we try and equate a to say 300, we get a different error message. This overflow error is reserved exclusively for enums. Others not invited.