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.