Introduction
This article will review some basic concepts of the C# language and present examples of advanced programming with enums. The aim is to review enums in detail and to use this concept of the language to produce better quality code in everyday programming.
Enums
The enum
keyword is used to declare an enumeration; there exists two enum types:
- Standard enums
- Bit flag enums
The default underlying type is int
. By default, the first enumerator has the value 0 and the value of each element is increased by 1.
enum Color {White, Red, Blue, Green, Black};
The Color
enum enumerators are White
=0, Red
=1,…, Black
= 4.
You can override the first enumerator value, for example:
enum Color {White=2, Red, Blue, Green, Black};
Now, the enumerator values are White
=2, Red
=3,…, Black
= 6.
You also can set every enumerator's value.
To extract the default values from an enum, you can use the default
keyword, for example:
enum Color {White=2, Red, Blue, Green, Black};
Color myColor = default(Color);
Console.WriteLine("Color = {0}", myColor);
Here is the output:
Color = 2
Enum data type
Each enum has an underlying type; the default type is int
, and the approved types are byte
, sbyte
, short
, ushort
, uint
, long
, and ulong
. For example, if you want to use less memory for an enum, you can use byte
, or long
to increase the size for enumerator values.
enum Color :byte { White, Red, Blue, Green, Black };
enum Limite :long {Down = 0L, Up = 2147483648L};
You can assign any value in the range of the underlying enum to a variable of type Colors
, they are not limitated to named constants.
Enum bit flags and FlagsAttribute
Using enumerators as bit flags can be useful to combine options or settings without creating separate properties. To enable bitwise combination of enumerators, you must start by setting up each enumerator with a bit mask flag. Look at the binary comments to understand why we are using 1, 2, 4.
public enum DocumentPermissions {
Modify = 1,
Read = 2,
Write = 4,
FullControl = Modify | Read | Write
};
So, if we want to check if an enum variable "permission
" is equal to DocumentPermissions.Modify
, DocumentPermissions.FullControl
, or DocumentPermissions.Read
, we can use the code below:
public class PermissionValidator {
public static bool CheckModify (DocumentPermissions doc) {
return ((int)doc & (int)DocumentPermissions.Modify) == 1;
}
}
DocumentPermissions permission = DocumentPermissions.Modify;
Console.WriteLine ("Result 1 = {0}", PermissionValidator.CheckModify (permission));
permission = DocumentPermissions.FullControl;
Console.WriteLine ("Result 2 = {0}", PermissionValidator.CheckModify (permission));
Console.WriteLine ("Result 3 = {0}", PermissionValidator.CheckModify (permission));
Here is the output:
Result 1 = True
Result 2 = True
Result 3 = False
Now, we will see a logical operation using '&', the logical operation 'AND'. The operation below uses the same values as the code:
Below is an illustration of the Flags
attribute using ToString()
:
[Flags]
public enum DocumentPermissionsFlag {
Modify = 1,
Read = 2,
Write = 4,
FullControl = Modify | Read | Write
};
Without the Flags
attribute, calling ToString()
produces the following output:
DocumentPermissions permission =
DocumentPermissions.Modify | DocumentPermissions.Write;
Console.WriteLine("Output 1 = {0}", permission.ToString());
Here is the output:
Output 1 = 5
Now, with the Flags attribute, ToString()
produces the following output:
DocumentPermissionsFlag permissionFlag =
DocumentPermissionsFlag.Modify | DocumentPermissionsFlag.Write;
Console.WriteLine ("Output 2 = {0}", permissionFlag.ToString());
Here is the output:
Output 2 = Modify, Write
Cast int and string to enum
The casting of a string
or an int
to an enum
can be useful to convert data from different data sources. For example, data from a database, XML file attributes, or a web page query string can be bound to an enum.
Below is an example of a string
to enum
conversion; this cast is performed using the static method Parse
from the enum
object:
DocumentPermissions permission =
(DocumentPermissions)Enum.Parse (typeof (DocumentPermissions), "Write");
Console.WriteLine ("Output {0}", permission.ToString ());
Now, an example of an int
to enum
conversion; it uses the ToObject
method of the enum
object. With the int
type, you can also use a simple cast (see equivalent to):
permission =
(DocumentPermissions)Enum.ToObject (typeof (DocumentPermissions), 4);
Console.WriteLine ("Output {0}", permission.ToString ());
permission = (DocumentPermissions) 4;
The two previous calls may cause an error if the string
or the int
were not an element of the enum. To check whether an item is not part of an enum, we can use the static method IsDefined
from the Enum
class.
In the example below, I use IsDefined
to control if the element is contained in the enum; in this case, I throw a ArgumentOutOfRangeException
.
Now, we will discuss about a generic cast. To create a method that casts a string or an integer to any type of enum, we can use the T
keyword.
Since version 2 of the .NET Framework, it is possible to have one method that returns the deferent’s data type. Below is an example with the type string
.
public static T CastToEnum<T> (string name) {
if (Enum.IsDefined (typeof (T), name)) {
return (T)Enum.Parse (typeof (T), name);
}
else {
throw new
ArgumentOutOfRangeException (
string.Format ("{0}...{1}", name, typeof (T).ToString()));
}
}
public enum Animal {
Dog = 1,
Cat,
Mouse,
Bird,
Tiger
};
Animal animal = EnumHelper.CastToEnum<Animal> ("Dog");
Console.WriteLine ("Output {0}", animal.ToString ());
Here is the output:
Output Dog
In the source code of the article, you will find a class EnumHelper
that contains two methods that cast an int
and a string
to any type of enums.
Enum enumerators
We have the possibility to enumerate an enumerator’s names and values using the Enum
class; with the same methods, you can count the number of elements from the enum. Below is an example of using the GetNames
static method from the Enum
class:
foreach (string name in Enum.GetNames (typeof (Animal))) {
System.Console.WriteLine (name);
}
Here is the output:
Dog
Cat
Mouse
Bird
Tiger
Now, an example enumerating the enum values using GetValues
:
foreach (int name in Enum.GetValues (typeof (Animal))) {
System.Console.WriteLine (name);
}
Here is the output:
1
2
3
4
5
The same method can count the items on an enum. The GetValues
returns an array containing the values of the items defined in the enumeration. We can then check the array’s length.
System.Console.WriteLine ("Count = {0}",
Enum.GetValues (typeof (Animal)).Length);
Here is the output:
Count = 5
VS 2005-2008 code completion
You can easily create a « switch » from an enum variable with Visual Studio 2005-2008.
- Start by creating the enum variable as follows:
Animal animal;
- Type « switch » and press the « tab » key.
Note: Visual Studio create the enum skeleton and selects the "switch_on
" text.
switch (switch_on) {
default:
}
- Type « animal » and press Enter.
Note: The editor automatically creates the cases for the switch
.
switch (animal) {
case Animal.Dog:
break;
case Animal.Cat:
break;
case Animal.Mouse:
break;
case Animal.Bird:
break;
case Animal.Tiger:
break;
default:
break;
}
Be attentive to the default path; in many cases, the switch enum does not require a default path.
Good coding practices using enums
Using enums instead of hard coded values can improve your coding quality, but be careful of design mistakes.
Good practices
- Prefer enums to static constants.
- Use plural nouns for bit flag enums and singular nouns for standard enums.
- Use an enum for strongly typed parameters, properties, and return values that represent a set of values.
- If your enum contains a default value like
None
or NoValue
, set their value to 0.
- Apply the
[Flags]
attribute to bit flag enums.
- If you use a bit flag enum, provide enumerators for common flag combinations.
Bad practices
- Do not use enums if their content varies with code versions.
- Do not reserve values for future use.
- Do not create enums with only one enumerator.
- Do not apply the
[Flags]
attribute to standard enums.
- Do not mix different concepts in a unique enum.
- Do not use the 0 value with a bit flag enum, the 0 always creates false values (0 AND x = 0).
Conclusion
I hope that this article will help you to improve your code; if you have any questions or remarks, please add a comment. I will respond as soon as possible.
History
- Nov 27, 2008 - Version 1.1 - Update regarding comments.
- Added VS 2005 for code completion.
- Removed the statement that enum values are distinct.
- Added cast information.
- Nov 26, 2008 - Version 1.0 - Initial version.