Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Everyday programming techniques - C# Enums

0.00/5 (No votes)
26 Nov 2008 1  
This article reviews basic and advanced programming with enums (flags, bit flags cast, and more).

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.

// Use byte enumerators

enum Color :byte { White, Red, Blue, Green, Black };
 
// Use long enumerators

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,     // 001
         Read = 2,       // 010
         Write = 4,      // 100
         FullControl = Modify | Read | Write    // 111     
};

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;
         }
     }
// Modify
DocumentPermissions permission = DocumentPermissions.Modify;
Console.WriteLine ("Result 1 = {0}", PermissionValidator.CheckModify (permission));
 
// FullCOntrol
permission = DocumentPermissions.FullControl;
Console.WriteLine ("Result 2 = {0}", PermissionValidator.CheckModify (permission));
 
// Read permission = DocumentPermissions.Read;
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:

csharpenums1.png

Below is an illustration of the Flags attribute using ToString():

[Flags]
public enum DocumentPermissionsFlag {
         Modify = 1,     // 001
         Read = 2,       // 010
         Write = 4,      // 100
         FullControl = Modify | Read | Write    // 111
     }; 

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 ());

// Equivalent to
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 {  //No enum for string name
         throw new
              ArgumentOutOfRangeException (
                string.Format ("{0}...{1}", name, typeof (T).ToString()));
     }
}

 
//Animal enum
public enum Animal {
         Dog = 1,
         Cat,
         Mouse,
         Bird,
         Tiger
     };
 
// Use EnumHelper
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.

  1. Start by creating the enum variable as follows:
  2. Animal animal;
  3. Type « switch » and press the « tab » key.
  4. Note: Visual Studio create the enum skeleton and selects the "switch_on" text.

    switch (switch_on) {
        default:
    }
  5. Type « animal » and press Enter.
  6. 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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here