Chapter 10
IN THIS CHAPTER
Finding real-world examples of enumerations
Creating and using enumerations
Using enumerations to define flags
Using enumerations as parts of switches
To enumerate means to specify individual items, as in a list. For example, you might create an enumeration of colors and then list individual colors, such as red, blue, green, and so on. Using enumerations in programming makes sense because you can list individual items as part of a common collection. For example, Colors.Blue
would indicate the color blue, and Colors.Red
would indicate the color red. Because enumerations are so handy, you see them used all the time in the actual world, which is why you also see them in applications. Code should model the real world to provide useful functionality in an easy-to-understand form.
The enum
keyword lets you create enumerations in C#. This chapter begins by discussing basic enum
usage but then moves on to some interesting additions you can make. For example, you can use initializers to determine the initial value of each enumeration element.
Flags give you a compact way to track small configuration options — normally on or off, but you can make them more complicated than that. You see them used a lot in older applications because they make memory usage significantly more efficient. C# applications use flags to group like options and make them easier to find and work with. You can use a single flag variable to determine precisely how some objects work.
Enumerations also see use as part of C# switches. Book 1, Chapter 5 introduces you to the switch
statement, but this chapter takes you a little further by demonstrating how using enumerations can make your switch
statements even easier to read and understand.
A problem with many programming constructs is relating them to the real world, and such can be the case with enumerations. An enumeration is any permanent collection of items. As previously mentioned, colors are one of the more common enumerations, and you use them often in the real world. However, if you were to look up color enumerations online, you’d find a stack of programming-specific references and not a single real-world reference. Instead of a color enumeration, look for a color wheel; that’s how people in the real world enumerate colors and create collections of color types. People often organize the color sets by their position on the color wheel, such as complementary or analogous colors. (See https://www.sessions.edu/color-calculator/
for a color calculator and description of the color wheel.)
Collections take many forms, and you may not even realize that you've created one. For example, the site at https://www.iberdrola.com/sustainability/biology-kingdoms-living-things-classification
tells you about the classification of living organisms. Because these classifications follow a pattern and tend not to change much, you could express them as an enumeration within an application. For example, the list of five kingdoms is unlikely to ever change. Even the list of phylums within each kingdom is unlikely to change, so you could express them as enumerations as well.
Practical, everyday uses for enumerations include lists of items or information that everyone needs. For example, you can’t mail something without knowing which state the package is supposed to go to. An enumeration of states within an application saves everyone time and ensures that the address appears without errors. You use enumerations to represent actual objects correctly. People make mistakes, and enumerations reduce errors; also, because they save time, people really want to use them.
The basic idea behind enumerations is relatively simple. All you really need to do is create a list of names and assign the collection a name. However, you can make additions to a basic enumeration that enhances flexibility and your ability to use enumerations in a wide range of scenarios. The following sections use the Enumerations
example to describe how to create various kinds of enumerations.
You use the enum
keyword to create an enumeration. For example, the following code creates an enumeration named Colors
.
enum Colors {Red, Orange, Yellow, Green, Blue, Purple};
// Display the color name.
Console.WriteLine($"Color Name: {Colors.Blue}.");
// Display the color value.
Console.WriteLine($"Color Value: {(int)Colors.Blue}.");
If you were to execute this code, you'd see Blue
as the output for the first line and 4
for the output of the second line. When creating a default enumeration setup, the values begin at 0 and proceed sequentially from there. Because Blue
is the fifth element of Colors
, its value is 4
. (Initializers, discussed in the next section, allow you to change the value default.)
You might need to access the entire list of enumerated values at some point. To perform this task, you use a foreach
statement, like this:
// Display all the elements starting with names.
Console.WriteLine("
All Color Names:");
foreach (String Item in Enum.GetNames(typeof(Colors)))
Console.WriteLine(Item);
// Display the values too.
Console.WriteLine("
All Color Values:");
foreach (Colors Item in Enum.GetValues(typeof(Colors)))
Console.WriteLine($"{Item} = {(int)Item}");
However, you might actually need only a range of values. In this case, you could also use a for
statement, like this:
// Display a range of names.
for (Colors Item = Colors.Orange; Item <= Colors.Blue; Item++)
Console.WriteLine("{0} = {1}", Item, (int)Item);
In this case, you see only a range of the values that Colors
provides. The output is
Orange = 1
Yellow = 2
Green = 3
Blue = 4
Using the default values that the enum
keyword provides works fine in most cases because you don't really care about the value — you care about the human-readable form of the value. However, sometimes you really do need to assign specific values to each of the enumeration elements. In this case, you need an initializer. An initializer simply specifies the specific value assigned to each element member, like this:
enum Colors2
{
Red = 5,
Orange = 10,
Yellow = Orange + 5,
Green = 5 * 4,
Blue = 0x19,
Purple = Orange | Green
}
To assign a value to each element, just add an equals sign, followed by a value. You must provide a numeric value. For example, you can’t assign a value of "Hello"
to one of the elements.
// Display Colors2.
Console.WriteLine("
Display Colors with a Specific Value:");
foreach (Colors2 Item in Enum.GetValues(typeof(Colors2)))
Console.WriteLine($"{Item} = {(int)Item}");
Here are the results:
Red = 5
Orange = 10
Yellow = 15
Green = 20
Blue = 25
Purple = 30
The default enumeration data type is int
. However, you might not want to use an int
; you might need some other value, such as a long
or a short
. You can, in fact, use the byte
, sbyte
, short
, ushort
, int
, uint
, long
, and ulong
types to create an enumeration. The type you choose depends on how you plan to use the enum
and how many values you plan to store in it.
To define an enum
data type, you add a colon and type name after the enumeration name. Here's an example:
enum Colors3: byte {Red, Orange, Yellow, Green, Blue, Purple};
The Colors3
enumeration is supposedly of type byte
. Of course, you don't know for certain that it is until you test it. The following code shows how to perform the testing:
// Display Colors3.
Console.WriteLine("
Display Byte-sized Colors:");
foreach (Colors3 Item in Enum.GetValues(typeof(Colors3)))
Console.WriteLine($"{Item} is {Item.GetTypeCode()} = {(int)Item}");
Red is Byte = 0
Orange is Byte = 1
Yellow is Byte = 2
Green is Byte = 3
Blue is Byte = 4
Purple is Byte = 5
Flags provide an interesting way to work with data. You can use them in various ways to perform tasks such as defining options that aren’t exclusive. For example, you can buy a car that has air conditioning, GPS, Bluetooth, and a number of other features. Each of these features is an addition, but they all fall within the category of optional accessories.
0000 0001 Air Conditioning
0000 0010 GPS
0000 0100 Bluetooth
By reserving bit positions and associating them each with a particular option, you can start to perform bit manipulation using and (&
), or (|
), and exclusive or (^
). For example, a value of 3, which equates to 0000 0011, would tell someone that a buyer needs both air conditioning and GPS.
[Flags]
enum Colors4
{
Red = 0x01,
Orange = 0x02,
Yellow = 0x04,
Green = 0x08,
Blue = 0x10,
Purple = 0x20
}
Note the [Flags]
attribute that appears immediately before the enum
keyword. An attribute tells the C# compiler how to react to a common structure in a special way. In this case, you tell the C# compiler that this isn't a standard enumeration; this enumeration defines flag values.
// Create a variable containing three color options.
Colors4 myColors = Colors4.Red | Colors4.Green | Colors4.Purple;
// Display the result.
Console.WriteLine("
Work with Color Flags:");
Console.WriteLine(myColors);
Console.WriteLine("0x{0:X2}", (int)myColors);
The code begins by creating myColors
, which contains three options, Colors4.Red
, Colors4.Green
, and Colors4.Purple
. To create an additive option list, you always or the values together using the |
operator. Normally, myColors
would contain a value of 41
. However, the next two lines of code show the effects of the [Flags]
attribute:
Red, Green, Purple
0x29
The output shows the individual options when you display myColors
. Because myColors
represents flag values, the example also outputs the myColors
value of 41
as a hexadecimal value of 0x29
. The addition of the X2
format string to the format argument outputs the value in hexadecimal, rather than decimal form with two significant digits. The format argument and the format string are separated with a colon (:
). You can read more about format types (which include format strings) at https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings
.
When working with a switch
statement, the reason for a decision can be quite unclear if you use a numeric value. For example, the following code doesn't really tell you much about the decision-making process:
// Create an ambiguous switch statement.
int mySelection = 2;
switch (mySelection)
{
case 0:
Console.WriteLine("You chose red.");
break;
case 1:
Console.WriteLine("You chose orange.");
break;
case 2:
Console.WriteLine("You chose yellow.");
break;
case 3:
Console.WriteLine("You chose green.");
break;
case 4:
Console.WriteLine("You chose blue.");
break;
case 5:
Console.WriteLine("You chose purple.");
break;
}
This code leaves you wondering why mySelection
has a value of 2
assigned to it and what those output statements are all about. The code works, but the reasoning behind it is muddled. This is also a good way to create a hard to find bug. To make this code more readable, you can use an enumerated switch, like this:
// Create a readable switch statement.
Colors myColorSelection = Colors.Yellow;
switch (myColorSelection)
{
case Colors.Red:
Console.WriteLine("You chose red.");
break;
case Colors.Orange:
Console.WriteLine("You chose orange.");
break;
case Colors.Yellow:
Console.WriteLine("You chose yellow.");
break;
case Colors.Green:
Console.WriteLine("You chose green.");
break;
case Colors.Blue:
Console.WriteLine("You chose blue.");
break;
case Colors.Purple:
Console.WriteLine("You chose purple.");
break;
}
The output is the same in both cases: “You chose yellow.” However, in the second case, the code is infinitely more readable. Simply by looking at the code, you know that myColorSelection
has a color value assigned to it. In addition, the use of a Colors
member for each case
statement makes the choice clear. You understand why the code takes a particular path.
It's possible to extend enumeration functionality using methods. For example, you might want to create a special formatting of a Colors
enumeration like this:
public enum Colors { Red, Orange, Yellow, Green, Blue, Purple};
public static class Extensions
{
public static string GetNameValue(this Colors color)
{
return $"{color} is {color.GetTypeCode()} = {((long)color)}";
}
}
The Extensions
class contains the GetNameValue()
method that accepts a Colors
object as input and outputs a specially formatting string
. The code should look somewhat familiar because it's based on the code found in the “Specifying an enumeration data type” section, earlier in this chapter. However, the use of the Extensions
class changes how you work with Colors
. You can list the colors in the enumeration like this now:
static void Main(string[] args)
{
// Display each of the colors in turn.
foreach (Colors color in Enum.GetValues(typeof(Colors)))
Console.WriteLine(color.GetNameValue());
}