Coding With Flavio
Lesson Summary:
Enums
Enumerations, or "enums" as they are often called, are special code structures that allow you to define a type which has a specific set of fixed values. For example, imagine defining an integer type that only allows the values 0, 1, and 2, except these values are called something specific and descriptive, such as "Apple", "Banana" and "Orange". This would essentially be an enum.
The following defines an enum with three possible values.
enum Fruit
{
Apple,
Banana,
Orange
};
This enum can be used to represent some sort of fruit. Note that this is similar to a class definition, in which we basically define the enum, but don't actually create an instance of one (it's like a schematic for this enum). We can actually use it this way:
static void Main(string[] args)
{
Fruit fruit1 = Fruit.Apple;
Fruit fruit2 = Fruit.Apple;
Fruit fruit3 = Fruit.Orange;
}
Here we have 3 variables of type "Fruit". The first two have the value Apple and the third has the value Orange. We can do something like this:
Fruit fruit1 = Fruit.Apple;
if (fruit1 == Fruit.Apple)
{
// Do stuff
}
Basically this type which we created can be used like other types which we have learned about, but its values are restricted to what is defined. For example, given our definition of Fruit above, the compiler will complain about the following line:
Fruit fruit4 = Fruit.Pear; // Compiler will give error saying Pear is not a valid value
This is largely the sort of reason why enum is often preferable to an alternative such as having each fruit variable be a string, such as:
string fruit1 = "Apple";
string fruit2 = "Apple";
string fruit3 = "Orange";
Among other reasons which I will cover soon, this example above has the problem that I can define a fruit which is not one of the "valid" options:
string fruit4 = "Pear"; // Compiler will not complain, which is not good if Pear is not a "supported" value
Programming is often about creating restrictions in the code to lower the room for error. As such, if we use a string instead of an enum, we will have to handle cases like the one above. Furthermore, we run into separate issues such as case sensitivity, as well as a general lack of information about which strings are valid, all of which can be prevented by using an enum instead.
Switch
A switch statement is very similar to a chain of if/elseif blocks. It's best described using an example:
int num = 6;
switch (num)
{
case 1:
Console.WriteLine("Just one");
break;
case 6:
Console.WriteLine("Half dozen!");
break;
case 12:
Console.WriteLine("A dozen!");
break;
default:
Console.WriteLine("Some other amount");
break;
}
When you do the "switch", you specify a value, such as num above. Each "case" statement corresponds to one possible value that you want to handle. default is akin to else in a chain of if/elseif statements: it handles everything that was handled neither by if or elseif.
In the above example, we print "Half dozen!" to the console because num has a value of 6. Based on the value, it will go into a different case block, or into the default block if none of the cases match the value. You may notice how this compares well to a set of if/elseif/else statements chained together:
int num = 6;
if (num == 1)
{
Console.WriteLine("Just one");
}
else if (num == 6)
{
Console.WriteLine("Half dozen!");
}
else if (num == 12)
{
Console.WriteLine("A dozen!");
}
else
{
Console.WriteLine("Some other amount");
}
Note the use of "break" in the example for the switch we used above. At the end of each case block, you must put a break, which specifies the end of the statement, after which you will exit the entire switch statement. This is required even for the last statement. The reason for this odd feature is that switch allows for one ther thing: cascading. See this example:
switch (num)
{
case 1:
break;
case 2:
case 3:
case 5:
case 7:
Console.WriteLine("Prime!");
break;
case 4:
case 6:
case 8:
Console.WriteLine("Non-prime even!");
break;
default:
Console.WriteLine("Something else!");
break;
}
Here you can see that you can specify multiple case statements that correspond to the same block. Any of 2, 3, 5 or 7 here will result in "Prime!" being printed to the console. For the sake of illustration, a value of 1 will do nothing, 4, 6 or 8 will print "Non-prime even!", and any other number will print "Something else!".
Switch statements are particularly useful with enums. See the following example:
enum Fruit
{
Banana,
Cherry,
Apple,
Orange
};
We can use a similar setup from above for this enum:
Fruit myFruit = Fruit.Apple;
switch (myFruit)
{
case Fruit.Apple:
case Fruit.Cherry:
Console.WriteLine("Red!");
break;
case Fruit.Orange:
Console.WriteLine("Orange!");
break;
case Fruit.Banana:
Console.WriteLine("Yellow!");
break;
}
This setup is particularly useful because the limited number of explicit states associated with switch goes well together with the limited set of values for enums.
Lesson FAQ
Why can't I use variables or expressions in my switch's cases?
You might notice that one of the primary limitations of switch is that you cannot use expressions (ie you cannot say "case num > 5" or something of the sort). For this, you should use an if statement instead. That is because the values in each case statement must be a constant. I did not yet speak at length about constants, but as an example, these are valid (for different switch statements of course):
case 5:
case "abc":
case Fruit.Apple:
Note that this is not valid:
int i = 5;
int value = 0;
switch (value)
{
case i: // i is not constant; compiler will complain
break;
}
However, this is valid:
const int i = 5;
int value = 0;
switch (value)
{
case i: // i is constant; won't complain
break;
}
The only difference is that i is marked constant via the keyword "const". I will probably discuss constants more in the future, but they are basically a variable whose value is set only once (when created) and will never change through the program's runtime.
Practice: Cards
A handy construct that makes it easy to create your own type which defines a fixed set of descriptive values.