Value returned to indicate an invalid value

Let's say you have a function that returns an enum:

public enum ServerStatus
{
    Down,
    Up
}

private ServerStatus GetServerStatus(int time)
{
    if (time >= 0 && time < 12)
    {
        return ServerStatus.Down;
    }
    else if (time >= 12 && time <= 23)
    {
        return ServerStatus.Up;
    }
    else
    {
        return ?? // Server status is neither Up nor Down
    } 
}

      

Should I:

  • Add "Nothing" to ServerStatus
  • Returns GetServerStatus ServerStatus? and return null
  • Returns another bool indicating whether the value has a value
+1


a source to share


5 answers


If this case should never happen in practice, and you don't expect to create anything other than "Up" and "Down", then you should throw an exception.



+13


a source


If you don't want to throw an exception, you can always have an unknown enum value. For example, the server status can be Up, Down, or Unknown. Unknown status may be valid in some cases (for example, if you cannot get a connection to the server). This unknown value can be tested as a boolean value to decide if the answer actually makes sense.



I'm not saying it's better than ArgumentOutOfRangeException, it's just another way to deal with this problem. Your calling code can always throw an exception depending on other criteria.

+2


a source


private ServerStatus GetSeverStatus(int time)
{
    if (time >= 0 && time < 12)
    {
        return ServerStatus.Down;
    }

    if (time >= 12 && time <= 23)
    {
        return ServerStatus.Up;
    }

    throw new ArgumentOutOfRangeException();
}

      

+2


a source


This is a case of "how can I enforce my prerequisites". You have several ways to do this.

One of the ways I really appreciate is: a very simple "programmed by contract" function that handles pure business logic: assert the correct precondition. Build a "safe" function above it that ensures that the input is valid.

// with a 'validity' return type
public Pair<bool,ServerStatus> checkedGetServerStatus( uint time ) {
   Pair<bool,ServerStatus> p;
   p.first = false;
   if( time > 23 ) return p;

   p.first = true;
   p.second = GetServerStatus( (int)time );
   return p;
}

// or with an exception
public ServerStatus GetServerStatusEx( uint time ) {
   if( time > 23 ) throw InvalidArgumentException(time);

   return GetServerStatus( (int)time );
}

      

Another, maybe better approach would be to change the argument type of the supplied function to something that cannot be an error.

class ServerTime {
    public ServerTime( uint hours ) { seconds = (hours%24)*3600; }
    private uint seconds;
}

public ServerStatus GetServerStatus( ServerTime time ) {
...
}

      

+1


a source


My thinking would be like this:

If it is an exceptional case (1 in 10,000 calls, once a year?) Or a fatal error, then an exception is thrown.

Unless the "unknown" case is exceptional or it is a recoverable error, I would go with the "unknown" value. The question then becomes, are you adding a third value or are you using a nullable type?

If likely, add a third "unknown" or "unreachable" state.

If it is not, then switch to a nullable type.

Whichever solution you are using, you may need some way to figure out why you are unable to determine the server status - there might be no connection, user credentials might be invalid, there might be too many connections, etc. - to determine whether to try again immediately, try again after some delay, or simply abort.

0


a source







All Articles