16

Why do I get a "not all code paths return a value", for VeryBoolToBool() in the following code?

public enum VeryBool { VeryTrue, VeryFalse };
public bool VeryBoolToBool(VeryBool veryBool)
{
    switch(veryBool)
    {
        case VeryBool.VeryTrue:
            return true;

        case VeryBool.VeryFalse:
            return false;

        // Un-commenting the unreachable(?) default clause will solve this
        // default:
        //    throw new HowTheHellDidIGetHereException();
    }
}

Can't the compiler see there are no other options for VeryBool?

bavaza
  • 10,319
  • 10
  • 64
  • 103

2 Answers2

17

Can't the compiler see there are no other options for VeryBool?

Nope, because there are. For example, I could call:

VeryBoolToBool((VeryBool) 5);

Enums in C# are not limited sets of values. They're effectively named numbers, with additional compile-time type safety in that there aren't implicit conversions between enums or between enums and numbers. (There are explicit conversions though.) The explicit conversions do not ensure that the value in question is one with a name, however.

Beyond that, switch in C# never checks whether all possible values of the type are explicitly listed. The end of a switch statement is always deemed "reachable" unless there's a default case (and all cases terminate). More precisely, from the end of section 8.7.2 of the C# 5 specification:

The end point of a switch statement is reachable if at least one of the following is true:

  • The switch statement contains a reachable break statement that exits the switch statement.
  • The switch statement is reachable, the switch expression is a non-constant value, and no default label is present.
  • The switch statement is reachable, the switch expression is a constant value that doesn’t match any case label, and no default label is present.
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Hmm. I was wondering if this choice was aimed to ease the implementation of enums in .Net, or is there some benefit in doing stuff like Marc's `VeryBool val = (VeryBool)42;`? – bavaza Dec 24 '13 at 11:10
  • 4
    @bavaza: There are times when it's useful for enums to behave like this - but plenty of others where it's not. I wish there were a "strict enum" type in .NET, but for the moment we need to live with what we've got :( – Jon Skeet Dec 24 '13 at 11:12
  • 1
    The most common case where you would want this behavior is when your `bool` is being used as a bit field (for flags). If you used code [like this](http://stackoverflow.com/a/8462/18192), you could then use `MyEnum foo = MyEnum.First | MyEnum.Second`. – Brian Dec 24 '13 at 14:32
2

Since C# 8 you can use a switch expression to solve this:

public enum VeryBool { VeryTrue, VeryFalse };

public bool VeryBoolToBool(VeryBool veryBool)
{
    return veryBool switch
    {
        VeryBool.VeryTrue => true,
        VeryBool.VeryFalse => false
    };
}

Note that you will get a warning:

The switch expression does not handle some values of its input type (it is not exhaustive) involving an unnamed enum value. For example, the pattern '(Fruit)2' is not covered.

But the warning can be suppressed or configured in your editor config (CS8524).

You can still pass an invalid enum value:

VeryBoolToBool((VeryBool) 5);

But this will throw a SwitchExpressionException.

mawax
  • 184
  • 6