Enumeration Pattern
When you have a fixed number of elements to choose from, an enumeration is a suitable pattern for implementing references. An enumeration is a hard-coded collection of values. The enumeration
CourseLevels in the following code is an excellent example.
public enum CourseLevels
{
Beginner,
Advanced,
VeryHigh,
Expert
}
Using an enumeration is easy, as is shown in the next code example.
public class Course
{
…
private CourseLevels level = CourseLevels.Beginner;
public CourseLevels Level
{
get { return level; }
set { level = value; }
}
}
Enumeration is a very elegant pattern, when the property's value needs to be checked in your application code, as the following code example shows.
if (MyCourse.Level == CourseLevels.Beginner) { … }
Methods on the
Course class can be specified elegantly as follows.
public bool HasLevel(CourseLevels level)
{
return (Level == level);
}
It is also easy to retrieve all possible values of an enumeration. In C# a simple call to
CourseLevel.GetNames() or
CourseLevel.GetValues() will do. Although using an enumeration is very straightforward, the pattern has a few issues. When its values need to be displayed on screen, there is no possibility of using different values than the ones defined by the enumeration. For sure,
VeryHigh will appear as
VeryHigh in your drop-down list.
Furthermore, if somehow the value of an element needs to be changed or new elements need to be introduced, enumerations lack the flexibility to perform this operation without recompiling the application. And third, in .NET enumerations are implemented as value type, which means that there is no way of extending them.
Constant Collection Pattern
To get around this third issue, developers tend to use a second pattern for using references, which I call the constant collection. In this pattern the collection of possible values is implemented as a class with a number of constants defined as its fields, much like in the following code example. Such a class can easily be inherited from.
public class CourseLevels
{
public const string Beginner = "Beginner";
public const string Advanced = "Advanced";
public const string VeryHigh = "Very high";
public const string Expert = "Expert";
}
The constant collection pattern also alleviates the second issued raised by the enumeration pattern. When displaying values on screen the actual value of the constants can be used, thus allowing for
VeryHigh to be displayed as
Very high. However, in return, the constant collection pattern raises its own issues. First of all, one can only retrieve all possible values using reflection. This may not be very desirable.
Worse, constant collections are not always type safe, even if they appear to be at first sight. Because the constants are defined static, the constant collection is type safe in respect to specific checks, like this one:
if (MyCourse.Level == CourseLevels.Beginner) { … }
But beware: Methods on the
Course class will have to be specified with a signature as follows.
public bool HasLevel(string level)
{
return (Level == level);
}
Thus, there is no guarantee that only type-safe values are passed, as with an enumeration. Both statements in the following code example will return
true when
MyCourse has level
Advanced. And even worse, the third statement will not produce a compiler error, as one would hope, since
Easy is not a valid value.
bool hasLevel = MyCourse.HasLevel(CourseLevels.Advanced);
bool hasLevel = MyCourse.HasLevel("Advanced");
bool hasLevel = MyCourse.HasLevel("Easy");