In the last chapter, we learned about the struct, which is a C# type and is similar to a class, encapsulating data and functionality, that is, variables and methods. We saw that a struct can have a custom constructor, but will always have a default parameterless constructor
, and the struct can contain properties for accessing the private members. In terms of accessibility, we saw that the struct could be made readonly, which means all fields automatically become set as readonly. However, we learned that from C# 8 individual fields
could be set to have the readonly access, thereby leaving other fields to remain readable and writeable. Finally, we learned that structs are value types, whereas a class is a reference type.
We will now look at another structure called the enumeration, or enum as it is also called. Enumerations are essentially a group of integer values
, which have been assigned names with the aim of making the code more readable. In Chapter 10 on iteration, we talked about magic numbers and how they are not acceptable when we wish to have clean code. Well, the enumeration helps us avoid the use of “magic numbers” by assigning names to them.
We use the keyword enum to identify the enumeration, which is used to hold a set of named integer constants. In C# an enumeration is a value data type, which means it will not inherit and has its own values. As we know from constants, the values do not change, so when we declare the constants in an enumeration, they cannot be changed in the code. As we said earlier, the “nice thing” about using an enumeration to declare constants is that we use descriptive names
for the constants, thereby making them more user-friendly and helping the code to be more readable. We could say that they fit in well with the concept of clean code.
Defining an Enumeration
In its basic form, the simplest way to declare an enumeration is to give the enumeration a name and then list all the possible names in a set of braces after the enumeration's name. We use the enum keyword as a prefix to the enumeration name, so that the compiler understands that the definition is an enumeration. By default, an enumeration list makes the first item in the curly braces have a value of 0 with each remaining value being incremented by 1.
Enumeration Examples
Example 1
A declaration defining a constant
for every day of the week, which means 0 is Sunday, 1 is Monday, etc. as shown in the following:
//
// Summary:
// Specifies the day of the week.
public enum DayOfWeek
{
//
// Summary:
// Indicates Sunday.
Sunday = 0,
//
// Summary:
// Indicates Monday.
Monday = 1,
//
// Summary:
// Indicates Tuesday.
Tuesday = 2,
//
// Summary:
// Indicates Wednesday.
Wednesday = 3,
//
// Summary:
// Indicates Thursday.
Thursday = 4,
//
// Summary:
// Indicates Friday.
Friday = 5,
//
// Summary:
// Indicates Saturday.
Saturday = 6
}
Now we will see that this enumeration called DayOfWeek
can be used in a C# application as shown in the code snippet:
// Create a Policy renewal date
DateTime dt = new DateTime(2018, 7, 04);
Console.WriteLine("Policy is due for renewal on {0:d}", dt);
/*
Use the DayOfWeek enumeration to find the day of the
Day of the week
*/
Console.WriteLine("This date is a is {0}", dt.DayOfWeek);
We will use this example in our code later, but what we might be surprised to hear is that the DayOfWeek enumeration is part of .NET. So enumerations exist in the real world.
Example 2
A declaration defining a constant for every month of the year, which means 0 is Jan, 1 is Feb, etc.:
enum Month
{
Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
}
Example 3
A declaration defining a constant for every suit in a deck of cards, which means 0 is Diamonds, 1 is Hearts, etc.:
enum Suit
{
Diamonds, Hearts, Spades, Clubs
}
Example 4
A declaration defining a constant for every examination possibility, which means 0 is Pass, 1 is Fail, etc.:
enum Result
{
Pass, Fail, Resit
}
Example 5
A declaration defining a constant for insurance types, which means 0 is Home, 1 is Auto, etc.:
enum InsuranceType
{
Home, Auto, Travel, Computing, Jewellery
}
Enumerated Values: Use and Scope
In C# we declare an enumeration anywhere and, like other items declared within a class
, the methods of the class are able to use the values of the enumeration list. Our application code simply needs to use the name of the enumeration and the value of the item within the list.
Let's code some C# and build ourprogramming muscle.
Add a new project to hold the code for this chapter.
1.
Right-click the solution CoreCSharp.
2.
Choose Add.
3.
Choose New Project.
4.
Choose Console App from the listed templates that appear.
5.
Click the Next button.
6.
Name the project Chapter20 and leave it in the same location.
7.
Click the Next button.
8.
Choose the framework to be used, which in our projects will be .NET 6.0 or higher.
9.
Click the Create button.
Now we should see the Chapter20 project within the solution called CoreCSharp
.
10.
Right-click the Chapter20 project in the Solution Explorer panel.
11.
Click the Set as Startup Project option.
Notice how the Chapter20 project name has been made to have bold text, indicating that it is the new startup project and that it is the Program.cs file within it that will be executed when we run the debugging.
12.
Right-click the Chapter20 project.
13.
Choose Add.
14.
Choose Item.
15.
Choose Class.
16.
Name the class Enumerations.cs.
17.
Amend the code to have a different namespace and have the Month enumeration within this namespace, as shown in Listing 20-1.
namespace Enumerations
{
public enum Month
{
Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
} // End of enum
} // End of namespace
Listing 20-1
Declaring the enumeration in a different namespace
18.
Right-click the Program.cs file in the Solution Explorer window.
19.
Choose Rename.
20.
Change the name to MonthExample.cs.
21.
Press the Enter key.
22.
Double-click the MonthExample.cs file to open it in the editor window.
23.
Amend the code, as in Listing 20-2, to use the Enumerations namespace through the using statement on the first line, add the namespace for this class, and add the Main() method.
using Enumerations;
namespace Chapter20
{
internal class MonthExample
{
static void Main(string[] args)
{
} //End of Main() method
} // End of MonthExample class
} // End of Chapter20 namespace
Listing 20-2
Declaring the enumeration and Main() method
Now we will investigate some of the methods from the System.Enum abstract class that we can use when working with enumerations.
Enumeration Methods
Three of the methods
we can use with an enumeration are
The GetNames(Type) method
of the Enum class, which is used to return a String array with the names of the constants in the enumeration.
The GetName(Type, value) method
of the Enum class, which is used to return the name of the constant in the enumeration with the specified value.
The ToString() method
, which will convert the value of the enum instance to its equivalent string representation. This method may be an easier option than the GetName().
We will now set the starting point of our enumeration to be Jan, which is constant value 0, and then we will iterate the enumeration displaying each value of the enumeration to the console.
Console.WriteLine("Using iteration with hard coded value");
for (int counter = 0; counter < 12; counter++)
{
Console.Write($" {month++} ");
} //End of iteration
} //End of Main() method
Listing 20-3
Set starting value of the enumeration, iterate, and display items
In the code in Listing 20-3, we are iterating through our enumeration and displaying the items in the enumeration to the console. We use the name of the enumeration instance
, in this case month, and increment it. In the console output, as shown in Figure 20-1, we see the names of the items of the enumeration, converted to string. In a later example, we will use the “index
” of the item.
25.
Click the File menu.
26.
Choose Save All.
27.
Click the Debug menu.
28.
Choose Start Without Debugging.
29.
Press the Enter key to close the console window.
This works fine, but what about our discussion in a previous chapter about the “magic number”? The iteration of the enumeration is hard-coded to stop at less than 12, but 12 just appears. We know that 12 is the number of items in the Month enumeration, the length, so can we use the length in the iteration, in a similar manner to when we discussed arrays? Yes, indeed we can, but it is not just as easy as using Length. We effectively need to look at the enumeration as an array and then get the length of the array. We can do this using the Enum class
, which has a static method called GetNames(), which will return an array object to us. Finally, we can easily get the length of the array using the Length property. Our line of code to get the enumeration length, the number of names in the enumeration, is
int enumMonthLength = Enum.GetNames(typeof(Month)).Length;
Now we will add this line to our code and amend the iteration so that it refers to the enumeration length variable
we will create.
Console.WriteLine("Using an iteration based on the enum Length from GetNames");
int enumMonthLength = Enum.GetNames(typeof(Month)).Length;
for (int counter = 0; counter < enumMonthLength; counter++)
{
Console.Write($" {month++} ");
} //End of iteration
Console.WriteLine();
} //End of Main() method
Listing 20-4
Using the enum length
31.
Click the File menu.
32.
Choose Save All.
33.
Click the Debug menu.
34.
Choose Start Without Debugging.
Figure 20-2 shows we have the same output as before but we have cleaner code
.
35.
Press the Enter key to close the console window.
Using the foreach Iteration
We have used a for iteration
in our code application, but we could also have used the foreach iteration
, both of which we read about and used in Chapter 10 on iteration. In Listing 20-4 we used the GetNames() method to return a string array and then we used the Length property. If we use the foreach iteration, we remove the need to know the enumeration length.
int enumMonthLength = Enum.GetNames(typeof(Month)).Length;
for (int counter = 0; counter < enumMonthLength; counter++)
{
Console.Write($" {month++} ");
} //End of iteration
Console.WriteLine();
Console.WriteLine("Using a foreach iteration which" +
" handles the length for us");
// Returns a String[] array so type is String
foreach (String valueFound in Enum.GetNames(typeof(Month)))
{
Console.Write($" {valueFound} ");
} //End of foreach
Console.WriteLine();
} //End of Main() method
Listing 20-5
Foreach iteration
37.
Click the File menu.
38.
Choose Save All.
39.
Click the Debug menu.
40.
Choose Start Without Debugging.
Once again, we will see from Figure 20-3 that we have the same output, but in our code we did not need the length to be known, as the foreach construct handles this for us, but we might also suggest that we have cleaner code.
41.
Press the Enter key to close the console window.
We will now use the GetName() method
where we pass the enum object and the value, which in this example will be the counter value, and we will be given the constant name in the enumeration at that position.
42.
Amend the code, as in Listing 20-6, to add a new iteration that uses the GetName() method.
// Returns a String[] array so type is String
foreach (String valueFound in Enum.GetNames(typeof(Month)))
{
Console.Write($" {valueFound} ");
}
Console.WriteLine();
// Using GetName() to find name of the value
for (int counter = 0; counter < enumMonthLength; counter++)
Use the GetName() to find the constant name at a specific position
43.
Click the File menu.
44.
Choose Save All.
45.
Click the Debug menu.
46.
Choose Start Without Debugging.
Figure 20-4 shows the names that were assigned to the constants are displayed.
47.
Press the Enter key to close the console window.
Enumeration Values: GetValues()
We will now use the GetValues(Type) method
of the Enum class to return an array of the values in the enumeration, and then we can iterate the array to display the values. We will code a new iteration that uses the GetValues() method of the Enum class
to get the integer values rather than the names.
Console.WriteLine("Using a foreach iteration to display the Month values");
foreach (int integerFound in Enum.GetValues(typeof(Month)))
{
Console.Write($" {integerFound} ");
}
Console.WriteLine();
} //End of Main() method
} // End of MonthExample class
} // End of Chapter20 namespace
Listing 20-7
Use the GetValues() to find the constant values
49.
Click the File menu.
50.
Choose Save All.
51.
Click the Debug menu.
52.
Choose Start Without Debugging.
Figure 20-5 shows that the constant values are displayed using the GetValues() method.
53.
Press the Enter key to close the console window.
Assigning Our Own Values to the Enumeration
It is possible for us to assign different values to the months, for example, we may wish April to be month 0 as it might be the first month
of a tax year. We will now amend the existing enumeration to assign a value
to the Apr name, and then on executing our code, we will see the effect this has on the other names in the enumeration. We will see that the default values for the names after Apr have been automatically amended.
Jan, Feb, Mar, Apr = 4, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
};
Listing 20-8
In the Enumerations namespace, change the Apr constant
55.
Click the File menu.
56.
Choose Save All.
57.
Click the Debug menu.
58.
Choose Start Without Debugging.
We see from the output, as shown in Figure 20-6, that the months are displayed as their integer value starting with 0, then 1, then 2, and then 4, as we assigned the integer 4 to April. From this point on, all the values are altered to their new consecutive value, 5, 6, 7, 8, 9, 10, 11, and 12.
59.
Press the Enter key to close the console window.
In the code in Listing 20-8, one value was altered, but we could have assigned different values to more than one or all of the enumeration names. An example is shown in Listing 20-9.
If we know the integer value of an item in the enumeration, we can get the name that is associated with it. Remember what we said earlier, the GetNames() method
returns a string array of the names in the enumeration and the GetName() method is passed a value and uses the value to get the assigned name. The GetValues() method
returns an array of the values in the enumeration.
60.
Amend the code, as in Listing 20-10, to use the GetValues() and GetName() methods.
Console.WriteLine("Using a foreach iteration to display the Month values");
foreach (int integerFound in Enum.GetValues(typeof(Month)))
{
Console.Write($" {integerFound} ");
}
Console.WriteLine();
Console.WriteLine("Get the enumeration name from the value");
foreach (int integerFound in Enum.GetValues(typeof(Month)))
{
Console.WriteLine($"The integer value of {integerFound} is the value {Enum.GetName(typeof(Month), integerFound)}");
}
Console.WriteLine();
} //End of Main() method
} // End of MonthExample class
} // End of Chapter20 namespace
Listing 20-10
GetName() and GetValues() methods
61.
Click the File menu.
62.
Choose Save All.
63.
Click the Debug menu.
64.
Choose Start Without Debugging.
Figure 20-7 shows the output displaying the integer value in the enumeration alongside its equivalent assigned name. The value is coming from the GetValues() method, while the name is coming from the GetName() method.
65.
Press the Enter key to close the console window.
Sample Application Using Enumerations
We will now code an application
that will have three enumerations to hold data for
Five types of computer hardware
Three types of policy offered for computer hardware
Five factors required to calculate a quote (essentially 0, 1, 2, 3, 4, 5)
The enumerations are the constants that we will use in our code.
The application will also ask the user to input the
Hardware type
Policy type
Hardware value
The application will then calculate the monthly premium based on the formula
where the hardwareTypeFactor
is obtained from the logic
Laptop is 5, Large_Screen is 5, Desktop is 4, Printer is 3, Small_Screen is 2
where the policyTypeFactor
is obtained from the logic
Gold is 5, Silver is 3, Bronze is 2
where the hardwareValueFactor
is obtained from the logic
Hardware value divided by 5000
Note
This is just an example to reinforce some of the features of C# enumerations. The code has not been written with attention being made to clean code
. It is about reading the code as we enter it and understanding what is happening in terms of the enumeration concepts we have looked at.
1.
Right-click the Chapter20 project.
2.
Choose Add.
3.
Choose Class.
4.
Name the class ComputerInsurance.cs.
5.
Click the Add button.
6.
Amend the code, as in Listing 20-11, to create a Main() method within the class and import the Enumerations namespace.
using Enumerations;
namespace Chapter20
{
internal class ComputerInsurance
{
static void Main(string[] args)
{
} //End of Main() method
} // End of ComputerInsurance class
} // End of Chapter20
Listing 20-11
Three enumerations and a Main() method
7.
Amend the Enumerations.cs code, as in Listing 20-12, to create the additional enumerations we will be using.
namespace Enumerations
{
public enum Month
{
Jan, Feb, Mar, Apr = 4, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
Console.WriteLine($"Monthly premium for a {Enum.GetName(typeof(HardwareType), hardwareType)}({hardwareType}) {Enum.GetName(typeof(PolicyType), policyType)} ({policyType}) policy is ${monthlyPremiumAmount: 0.00}");
Console.WriteLine($"HardwareType enumeration at position {hardwareType} is {Enum.GetName(typeof(HardwareType), hardwareType)}");
Console.WriteLine($"PolicyType enumeration at position {policyType} is {Enum.GetName(typeof(PolicyType), policyType)}");
}
}// End of CalculateMonthlyPremium method
} // End of ComputerInsurance class
} // End of Chapter20 namespace
Listing 20-18
Method that calculates the monthly premium
14.
Amend the code, as in Listing 20-19, to add the calls to the methods from within the Main() method and assign them to method-level variables.
Right-click the Chapter20 project in the Solution Explorer panel.
16.
Choose Properties from the pop-up menu.
17.
Choose the ComputerInsurance class in the Startup object drop-down list.
18.
Close the Properties window.
19.
Click the File menu.
20.
Choose Save All.
21.
Click the Debug menu.
22.
Choose Start Without Debugging.
We will see in Figure 20-8 that the console has the request for information.
23.
Type 0, representing a Laptop, for the hardware type, as in Figure 20-8.
24.
Press the Enter key to move to the next menu.
25.
Type 0, representing a Gold policy, for the policy type, as in Figure 20-9.
26.
Press the Enter key to move to the next input request.
27.
Type 1000 for the laptop value, as Figure 20-10, and press the Enter key.
Figure 20-11 shows the console window displaying the monthly premium for our chosen hardware type
, policy type
, and the estimated value
.
28.
Press the Enter key to close the console window.
Are we sure this is the correct premium amount?
Laptop has a factor of 5.
Gold has a factor of 5.
1000 has a factor of 1000/5000 = 0.2.
MonthlyPremium is 5 X 5 x 0.2 = 5.00.
Figure 20-11 therefore does show the correct answer, so our code looks great. Let's try different options.
29.
Click the File menu.
30.
Choose Save All.
31.
Click the Debug menu.
32.
Choose Start Without Debugging.
We will see in Figure 20-12 that the console has the request for information.
33.
Type 4, representing a Small_Screen, for the hardware type
34.
Press the Enter key.
35.
Type 2, representing a Bronze policy, for the policy type .
36.
Press the Enter key.
37.
Type 100 for the screen value.
38.
Press the Enter key.
Figure 20-12 shows the console window displaying the monthly premium for our chosen hardware type, policy type, and the estimated value.
Are we sure this is the correct premium amount?
Small_Screen has a factor of 2.
Bronze has a factor of 2.
100 has a factor of 100/5000 = .02.
MonthlyPremium is 2 X 2 x 0.02 = 0.08.
Figure 20-12 therefore does show the correct answer, so our code looks great.
Now let's try to use a different way to “convert” an integer value to the corresponding name in an enumeration. This is just another option, so rather than using the
Enum.GetName(typeof(HardwareType), hardwareType)
we will use
(Enumeration Name)integer value
39.
Amend the monthly premium method to add the new casting style to the display lines, as in Listing 20-20.
Console.WriteLine("*****Casting the enumeration VALUE to the enumeration NAME*****");
Console.WriteLine($"HardwareType enumeration at position {hardwareType} is {(HardwareType)hardwareType}");
Console.WriteLine($"PolicyType enumeration at position {policyType} is {(PolicyType)policyType}"); }
}// End of calculateMonthlyPremium method
} // End of ComputerInsurance class
} // End of Chapter20 namespace
Listing 20-20
Casting style for conversion
40.
Click the File menu.
41.
Choose Save All.
42.
Click the Debug menu.
43.
Choose Start Without Debugging.
We will see in Figure 20-13 that the console has the request for information.
44.
Type 4, representing a Small_Screen, for the hardware type.
45.
Type 2, representing a Bronze policy, for the policy type.
46.
Type 100 for the screen value.
Figure 20-13 shows the console window displaying the monthly premium for our chosen hardware type, policy type, and the estimated value. It also confirms that the casting, (HardwareType)hardwareType and (PolicyType)policyType, has been successful.
Chapter Summary
So, finishing this chapter on enumerations, we can see that an enumeration is a value data type. We use an enumeration to declare constants with a descriptive name for the constants. In its basic form, an enumeration has a name, followed by a set of braces, which will contain a list of all the possible names. We use the enum keyword as a prefix to the enumeration name so that the compiler understands that the definition is an enumeration. By default, an enumeration will make the first item within the curly braces have a value 0, with each remaining value being incremented by 1. We can use the methods of the Enum abstract class to get the names and values of the constants, and we use GetName(), GetNames(), and GetValues() as well as the Length property.
Once again, another dive into an advanced feature of programming, which can be applied to our C# code helping make it more readable and easier to maintain. We should be immensely proud of our learning to date. In finishing this chapter, we have increased our knowledge further and we are advancing to our target.