I was thinking about how I was going to use some enums I made, how I was going to make sure they had valid values and how to call the class which would contain the necessary extensions methods. And then I thought: why not wrap the enum in a struct. I wrote up a little example code and it looks like this (ignore the fact that it's a very common day enum, I needed something as an example):
public struct Day
{
private enum DayE
{
Monday = 1,
Tuesday,
Wednesday,
Thursday,
Friday
}
private readonly DayE value;
public static Day Monday { get { return new Day( DayE.Monday ); } }
public static Day Tuesday { get { return new Day( DayE.Tuesday ); } }
public static Day Wednesday { get { return new Day( DayE.Wednesday ); } }
public static Day Thursday { get { return new Day( DayE.Thursday ); } }
public static Day Friday { get { return new Day( DayE.Friday ); } }
private Day( DayE value )
{
this.value = value;
}
public override string ToString()
{
switch( value )
{
case DayE.Monday:
return "mo";
case DayE.Tuesday:
return "tu";
case DayE.Wednesday:
return "we";
case DayE.Thursday:
return "th";
case DayE.Friday:
return "fr";
default:
throw new InvalidOperationException( "Can't convert 0 value" );
}
}
}
Some advantages of this approach I could think of:
- Allows you to limit the possible values, only the ones you construct using the private constructor can be made. (Of course, the 0 value can always be constructed using
default(Day)
ornew Day()
.) This means you only have to test if the value is not 0 to make sure it's valid. If 0 is a valid value, you don't have to test for validity. - No need for an helper class for the extension methods, you can just put them in the struct. Also, you can override the
ToString
method, as can be seen in the example. - In a similar fashion,
static
methods are now possible. - I think it should be equally fast to just using the enum, since the compiler will probably take away most of the wrapper. Unless, of course, you add functionality, like validity checks, to methods.
Disadvantages:
- Need to create public static properties for all enum values. Although this does allow you to create extra values from combinations of other values, that you don't want to put in the enum itself (useful for tweaking the
ToString
behavior of a enum with Flags attribute). - Need to define methods for all common enum functionality (for each wrapper you make), like
HasFlag
and==
,!=
,&
and|
operators. However, this gives you control over which of these methods/operators are available and it allows for validity checks inside them. It's possible to create one or more VS code snippets for this. - Visual Studio's auto complete will be a little less happy with this. With enums it usually immediately auto selects the enum type name when you type e.g.
Day x =
. With this you'll first have to (partially) type that. - To use this in a switch statement, you need to make the internal value and enum public, because structs don't work in a switch.
What do you think of this construction? Can you name any more disadvantages? Would you recommend me not to use this?
Edit: found a new disadvantage, see last point above.
default(Day)
and force consumers to acquire instances through the predefined properties, you could makevalue
nullable. This may or may not be desirable. \$\endgroup\$PrevDay()
andNextDay()
methods which take into account that the Monday comes after the Friday (and vice versa). These methods could actually be properties instead, which is impossible for enums. For validation, you could have different methods, e.g. if you have a Flags enum, one that validates if it's exactly one valid flag, and one that validates if it's a valid combination of flags. \$\endgroup\$