Chapter 10

Creating Lists of Items with Enumerations

IN THIS CHAPTER

Bullet Finding real-world examples of enumerations

Bullet Creating and using enumerations

Bullet Using enumerations to define flags

Bullet 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.

Remember You don't have to type the source code for this chapter manually. In fact, using the downloadable source is a lot easier. You can find the source for this chapter in the CSAIO4D2EBK01CH10 folder of the downloadable source. See the Introduction for details on how to find these source files.

Seeing Enumerations in the Real World

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.

Remember Enumerations work only under certain circumstances. In fact, situations arise in which you should most definitely not use an enumeration. The following list offers some rules of thumb to use when deciding whether to create an enumeration:

  • Collection stability: The collection must present a stable, unchanging list of members. A list of states is stable and unlikely to change frequently. A list of the top-ten songs on the Billboard chart isn’t stable and can change almost daily.
  • Member stability: Each member within the collection must also remain stable and present a recognizable, consistent value. A list of area codes, even though quite large, is also consistent and recognizable, so you could create such an enumeration, should you decide to do so. A list of people’s first names is a bad idea because people change the spelling of names constantly and add new names at the drop of a hat.
  • Consistent value: Enumerations exchange numeric values that a program can understand for word values that a human can understand. If the numeric value associated with a particular word changes, the enumeration won’t work because you can’t rely on a dependable association between the numeric value and the word used to represent it.

Working with Enumerations

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.

Using the enum keyword

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};

Remember C# offers multiple ways to access the enumeration. If you need just a single value, you can use the color name. The output you get depends on how you access the value, as shown here:

// 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

Creating enumerations with initializers

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.

Remember You might be thinking to yourself that those last four initializers look strange. The fact is that an initializer can equate to anything that ends up being an integer. In the first case, you add 5 to the value of Orange to initialize Yellow. Green is the result of a math equation. Meanwhile, Blue uses hexadecimal format instead of decimal. Finally, Purple is the result of performing a logical or of Orange and Green. You use the same techniques as before to enumerate an enum that uses initializers:

// 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

Specifying an enumeration data type

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}");

Remember Note that you must use the Item.GetTypeCode() method, not the Item.GetType() method, to obtain the underlying enumeration type. If you use Item.GetType() instead, C# tells you that Item is of type Colors3. Here's the output from this example:

Red is Byte = 0
Orange is Byte = 1
Yellow is Byte = 2
Green is Byte = 3
Blue is Byte = 4
Purple is Byte = 5

Creating Enumerated Flags

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.

Remember When working with flags, you must think in terms of bits. For example, most people would think of a byte as being able to hold values up to 256, or they might think of a byte as being eight bits long. However, what you need to think about when working with flags is that the byte can hold eight individual bit values. So, a value of 1 might indicate that the person wants air conditioning. A value of 2 might indicate a desire for GPS. Likewise, a value of 4 might indicate a need for Bluetooth — with all using bit positions, as shown here:

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.

Remember The most common way to work with bit values is using hexadecimal (although, you can also use binary), which can represent 16 different values directly, which equates to four bit positions. Consequently, 0x11 would appear as 0001 0001 in bit form. Hexadecimal values range from 0 through F, where A = 10, B = 11, C = 12, D = 13, E = 14, and F = 15 in decimal form. Here's an example of an enumerated flag:

[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.

Tip You should also see that the individual elements rely on hexadecimal initializers (see the “Creating enumerations with initializers” section, earlier in this chapter, for details). C# doesn’t require that you use hexadecimal initializers, but doing so makes your code significantly easier to read. The following code shows how an enumerated flag might work:

// 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.

Defining Enumerated Switches

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.

Working with Enumeration Methods

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

Remember As you can see, the use of GetNameValue() is straightforward and makes working with the enumeration easier. Notice that you don't supply a value to GetNameValue(), though, and that’s because the input argument is defined at this Colors color, which means to use the object itself as the input. Book 2, Chapter 3 tells you all about working with this.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.58.132.97