tags:

views:

952

answers:

6

We were having a debate if enums should have uninitialized values. For example. We have

public enum TimeOfDayType
{
   Morning
   Afternoon
   Evening
}

or

public enum TimeOfDayType
{
   None
   Morning
   Afternoon
   Evening
}

I think that there shouldn't be any none but then you have to default to some valid value on initialization. But others thought there should be some indication of uniitized state by having another enum that is None or NotSet.

thoughts?

+1  A: 

Depends how the type is used. It's often easier for users of the type not to have an "undefined" value, because you don't have to special-case one value. But if you need one (because values sometimes need to be in a state which is otherwise not any of the enumerated values) then you need one. You usually don't save any special-case code by using two enums instead of one.

It's a bit like asking whether you should use nullable types.

Steve Jessop
+8  A: 

Speaking of nullable types - I think they can be used to solve the problem of forcing/not forcing the initialization of an enum. Say we have

enum Color { Red, Blue }

And let's say you have a function:

void Draw(Color c);

That function says that it requires a valid Color. However, we could also have this function:

void Draw(Color? c);

That says that the function can handle not being passed a color (null would be passed to indicate "don't care").

Well, it's one alternative to None members.

Frank Krueger
Agreed, the cleanest solution is a nullable type. It maps to DB schemas well too.
Robert Wagner
It's unfortunate that enums in .NET don't force you to pass in a valid value at all. I could pass in (Color) 123123 which would compile and run just fine - the Draw method would have to do the validation :(
Jon Skeet
Probably an inheritance from C, where the compiler doesn't know whether an enum is used as a true enumerated type, or as a set of flags. So it throws its hands in the air and surrenders.
Steve Jessop
+1  A: 

Just adding to Franks answer, one of the only times I would opt for a 'None' item in an enum over nullable is when the enum is being used as flags. The 'None' item would be id of 0.

Robert Wagner
+2  A: 

A nullable enum was proposed as a solution in some of the previous answers. But a nullable enum has the disadvantage that it makes clients check for a null value every time they use the enum. On the contrary, if you have a default value of "None", you have the option to use a switch for the meaningful values and just ignore "None", without having to worry that the enum variable could be null.

Anyway, I think having a default value of "None" or making the enum nullable makes sense only if the enum is used as an argument in a default constructor for some class. And you have to ask yourself - shouldn't the objects of that class have some meaningful default value? Using your example with the TimeOfDayType enum - if you initialize an object with TimeOfDayType.None, you can't use it anyway before you change the value to Morning, Afternoon or Evening. So couldn't you say that the default is Morning instead of None? Or - which is even better - couldn't you create your objects after you already know which enum value they need? I think that if the issue is correctly tackled with at the early design stages, you shouldn't need a special default value for your enums at all.

Of course, all of the above is a generalization. Maybe it can't be applied to your particular scenario, so if you give some details about it, we could discuss the issue more thoroughly.

Boyan
That's a good analysis of the root cause. Always take a step back first.
Johann Gerell
+3  A: 

In the abscence of a "default" member, I think it's valuable to have a value representing the literal int 0.

No matter what, a given enum will be created with the literal value 0. The most straight forward case here is as a member of a struct. A C# struct will always have an empty default constructor that initalizes all fields to their default value. In the case of an enum, that will be the literal value 0. The question is how to handle it.

For me this is an issue of style: If the enum is not explicitly initialized to a value, should it be given an arbitrary valid value or a specific value indicating a lack of explicit initialization?

enum Color { Unknown, Red, Blue }
enum Color2 { Red,Blue }
struct Example<T> {
  Color color;
}

static void SomeMethod() {
  var v1 = new Example<Color>();
  var v2 = new Example<Color2>();
}

In the case of v1, if the color field is inspected it will explicitly be labeled as an uninitialized field. In v2 the field will simple be "Red". There is no way for a programmer to detect between and explicit set to "Red" or an implicit default value to "Red".

Another case where this causes a problem is doing a switch statement against an enum value. Lets slighly alter the definition of Color2.

enum Color2 { Red = 1, Blue = 2 }
static void SomeOtherMethod(p1 as Example<Color2>) {
  switch ( p1.color ) {
   case Color.Red: {} 
   case Color.Blue: {}
   default: {throw new Exception("What happened?"); }
  }
}

The switch handles every explicit value in the enum. Yet this code will fail for the default constructor of Example<Color2> and there is no way to supress this constructor.

This brings up a slighly more important rule: Have an explicit enum value for the literal value 0.

JaredPar
I'm not sure why you need to have an explicit enum value for 0. Just leave it out, and explicitly initialize the other values - so Red=1, Blue=2 here, for example. Same effect for the most part, but then Enum.GetValues etc won't include the "unknown" value.
Jon Skeet
I add an explicit value because it sets expectations appropriately. There can and will be 0 value'd instances of that enum. It's an un-avoidable consequence of using it as a value type member. I feel like it makes the code clearer to explicitly deal with the Unknown upfront.
JaredPar
0 values will show up as 0 though without an explicit value, and it means you don't need to explicitly remove it when looking at the list of "real" values. I guess it depends on the exact usage though.
Jon Skeet
My main purpose in making it explicit is the switch case. Often people will only list the values explicitly defined in the enum (or they use the switch snippet). For enum's like Color2 that leaves a hole in the code. True any number could be cast to the enum, but 0 can/will occur with no casts
JaredPar
While this question is dated, I just browsed a code example on the MSDN page for Attribute Class (http://msdn.microsoft.com/en-us/library/system.attribute.aspx) that prompted a search leading here. An enum is commented with a statement suggesting that the values begin at 1 to use the enum type's default value of 0 to represent an uninitialized variable.
Ajw
+3  A: 

I always set one of my enum literals to zero. This literal must not always be named "None" or "NotSet". It depends if there is a literal which acts very well as default.

I set one to zero because, enums (except nullable enums) are always initialized by the CLR in memory to zero. And if you don't define one of the literals this memory contains illegal values. Also when you use enums as flags. The Default value cannot be used to do bitwise compairisons. The result will always be zero.

When you enable FxCop it checks if you have defined a literal as default. Seems to be a "good practice" when they have a rule for that.

TomTom