6

This discussion is about a name of default value: C#: Should the default value of an enum be None or Unknown?

However, large amount of people I spoken with recently considered default enum values harmful, unnecessary and potentially leading to a bad practice.

As an example consider the following:

enum eJobStates
{
    JOB_STATE_INITIALISING,
    JOB_STATE_PROCESSING,
    JOB_STATE_DONE
};

It would not make sense for a job to be in say, JOB_STATE_UNKNOWN, but you can imagine that any structure that might be used for monitoring of said jobs could use such value.

Are there any best practices / rules of thumb concerning creating a default value when defining an enum? Should they be avoided at all cost whenever possible?

Community
  • 1
  • 1
ellimilial
  • 1,246
  • 11
  • 20
  • Well can you have your monitoring class 'extend' the enum by adding a MONITORING_STATE value? – dchhetri Dec 17 '12 at 20:34
  • I might not fully understand the suggestion. In this particular example MONITORING_STATE would still not make much sense from the job perspective and, as per [link](http://stackoverflow.com/questions/1804840/extending-enums-in-c) I cannot extend the enum in the monitoring class (which ideally uses eJobStates as a private variable). – ellimilial Dec 17 '12 at 20:43
  • 1
    Yes I realize that you cannot naturally extend enums. monitoring_state was just a name, but you could definitely use job_state_unknown. What I was suggestion was to add additional enum in the monitoring class. You can do this by taking the last enum value of eJobStates and adding 1 to it, and make it the beginning enum state value for the monitoring class. After all, with enums you are simply comparing ints that represent states. If you want something more concrete, then as suggested in the link you can either create a Enum class, or make a state object for each state. – dchhetri Dec 17 '12 at 21:04

1 Answers1

2

An invalid default value is basically a variant in your design. The object isn't valid when it's created. You should avoid that when it's reasonable. By no means should you avoid it "at all costs."

Some problems require you to start in a variant state. In that case, you have to reason about that invalid value mentally. If you avoid naming it you are actively making your code less expressive. Think about it in terms of communication between you and the person that will have to maintain the code later.

Dealing with it downwind is annoying. You start in a variant state, but by the time it's relevant you hope that it is no longer variant. The strategy I prefer is allow users to ignore the variant state and just throw when I've made a mistake.

namespace FooType {
  enum EnumValue {
    INVALID = 0
    ,valid
  };
}

struct Foo {
  Foo() : val(FooType::INVALID) {}
  FooType::EnumValue get() const {
    if (val == FooType::INVALID)
      throw std::logic_error("variant Foo state");
    return val;
  }
  FooType::EnumValue val;
};

This frees your users from having to reason about your variance, which is worth fighting for.

If you can't get away with that, I usually prefer to degrade to safe and unsafe interfaces.

struct Foo {
  Foo() : val(FooType::INVALID) {}
  bool get(FooType::EnumValue& val_) const {
    if (val == FooType::INVALID)
      return false;
    val_ = val;
    return true;
  }
  FooType::EnumValue get() const {
    FooType::EnumValue val_;
    if (!get(val_))
      throw std::logic_error("variant Foo state");
    return val_;
  }
  FooType::EnumValue get_or_default(FooType::EnumValue def) const {
    FooType::EnumValue val_;
    if (!get(val_))
      return def;
    return val_;
  }
  FooType::EnumValue val;
};

These sorts of interfaces are good for things like databases, where null values may be expected.

Tom Kerr
  • 10,444
  • 2
  • 30
  • 46