C# is a mostly statically-typed language, which has all the advantages and disadvantages associated with static typing. For a comparison / explanation of static and dynamic typing, see this question. But what it essentially boils down to is that static typing allows you to specify restrictions on values that can be checked during compilation, instead of runtime.
In light of that, the main reason to use enums would be to ensure that certain variables, parameters, etc., conform to a subset of values. For instance, if I declare a method like this:
public decimal GetRequiredWorkHours(days dayOfWeek)
Then it's clear that the method must be passed a day of the week, not any other kind of value. Thus, the following is a valid call:
var myHoursForFriday = GetRequiredWorkHours(days.Friday);
But the following examples are not:
... = GetRequiredWorkHours("Friday");
... = GetRequiredWorkHours("Bob");
... = GetRequiredWorkHours(new DateTime(2020, 07, 07));
The advantage is that these invalid calls are detected at compile time. In other words, I can't make a valid C# program that has one of these calls in it.
Had we instead declared the parameter as a string
...
public decimal GetRequiredWorkHours(string dayOfWeek)
...then the compiler would accept all of these:
... = GetRequiredWorkHours("Friday");
... = GetRequiredWorkHours("Frday"); // misspelling
... = GetRequiredWorkHours("Bob"); // not a valid day
... = GetRequiredWorkHours(null); // string is a reference type, unlike enums, so null is permitted
...even though only the first one is valid. The compiler has no idea what strings are valid, just that the method takes a string. So you will have to code it yourself inside the method to check for which strings are valid, and that check will occur at runtime. This allows for bugs to be introduced that aren't present when using a stronger-typed parameter.
In your example, calling Console.WriteLine(object)
with an enum has the advantage that you can't, for example, mis-type the day name. However, since the parameter is of type object
, and the method converts the object to a string via ToString()
, this type information is lost inside the WriteLine method - which is OK, because the WriteLine method just needs to know how to represent the value in text, it doesn't have to know anything about what the value actually means.
This logic applies to more than parameters. For instance, you can have variables that are strongly-typed to a day of the week, etc. And your IDE will allow you to rename enum elements much more easily than if you had used string constants.
Now, having said all that, enums are actually not the greatest example of strong typing in C#. That's because they are really just aliases for int
values (or another integer type if you specify it as such). Normally the integer for each enum value is implicit, but you can specify it explicitly, and some style guides suggest you do so. E.g.:
public enum days
{
Monday = 0,
Tuesday = 1,
Wednesday = 2,
Thursday = 3,
Friday = 4,
Saturday = 5,
Sunday = 6
}
This has some advantages - for instance, you can represent bit flags by specifying powers of 2 as the numbers. However, it also has some disadvantages, most importantly, that it's not just the specified enum values which are valid for a variable / expression of that type. Any integer can be put into that value, e.g.:
... = GetRequiredWorkHours((days) 17);
... = GetRequiredWorkHours((days) -1);
... = GetRequiredWorkHours(0);
There's also the issue that a new value could be introduced to the enum after a method that is used for it is written. While extremely unlikely in your example (I suspect the number and name of days in the week will not change in our lifetimes), other cases may frequently be updated.
To account for either of these scenarios, the method will have to double-check that the value passed to it is actually one it recognizes, e.g.:
public decimal GetRequiredWorkHours(days day)
{
switch (day)
{
case days.Monday:
case days.Tuesday:
case days.Wednesday:
case days.Thursday:
case days.Friday:
return 40;
case days.Saturday:
case days.Sunday;
return 0;
default:
throw new Exception("Unrecognized day");
}
}
While a day of the week is a good fit for an enum, other values may be better served by creating classes and/or subclasses. This allows the value to carry information in addition to its identity.