Switch statement without default when dealing with enumerations

17,405

Solution 1

Heck, the situation is far worse than just dealing with enums. We don't even do this for bools!

public class Test {        
  public string GetDecision(bool decision) {
    switch (decision) {
       case true: return "Yes, that's my decision";                
       case false: return "No, that's my decision"; 
    }
  }
}

Produces the same error.

Even if you solved all the problems with enums being able to take on any value, you'd still have this issue. The flow analysis rules of the language simply do not consider switches without defaults to be "exhaustive" of all possible code paths, even when you and I know they are.

I would like very much to fix that, but frankly, we have many higher priorities than fixing this silly little issue, so we've never gotten around to it.

Solution 2

Throw an exception in the default clause:

default:
    throw new ArgumentOutOfRangeException("decision");

This ensures that all possible paths are covered, while avoiding the logic errors resulting from adding a new value.

Solution 3

That's because the value of decision could actually be a value that is not part of the enumeration, for instance :

string s = GetDecision((Decision)42);

This kind of thing is not prevented by the compiler or the CLR. The value could also be a combination of enum values :

string s = GetDecision(Decision.Yes | Decision.No);

(even if the enum doesn't have the Flags attribute)

Because of that, you should always put a default case in you switch, since you can't check all possible values explicitly

Solution 4

public enum Decision { Yes, No}

public class Test
{
    public string GetDecision(Decision decision)
    {
        switch (decision)
        {
            case Decision.Yes:
                return "Yes, that's my decision";
            case Decision.No:
                return "No, that's my decision";
            default: throw new Exception(); // raise exception here.

        }
    }
}

Solution 5

The default is there to protect you. Throw an exception from the default and if anyone adds an extra enum, you're covered with something to flag it.

Share:
17,405
Mark
Author by

Mark

.NET Architect & Developer & Geek interested in software development practices and latest technologies.

Updated on June 07, 2022

Comments

  • Mark
    Mark almost 2 years

    This has been a pet peeve of mine since I started using .NET but I was curious in case I was missing something. My code snippet won't compile (please forgive the forced nature of the sample) because (according to the compiler) a return statement is missing:

    public enum Decision { Yes, No}
    
        public class Test
        {
            public string GetDecision(Decision decision)
            {
                switch (decision)
                {
                    case Decision.Yes:
                        return "Yes, that's my decision";
                    case Decision.No:
                        return "No, that's my decision";
    
                }
            }
        }
    

    Now I know I can simply place a default statement to get rid of the complier warning, but to my mind not only is that redundant code, its dangerous code. If the enumeration was in another file and another developer comes along and adds Maybe to my enumeration it would be handled by my default clause which knows nothing about Maybes and there's a really good chance we're introducing a logic error.

    Whereas, if the compiler let me use my code above, it could then identify that we have a problem as my case statement would no longer cover all the values from my enumeration. Sure sounds a lot safer to me.

    This is just so fundamentally wrong to me that I want to know if its just something I'm missing, or do we just have to be very careful when we use enumerations in switch statements?

    EDIT: I know I can raise exceptions in the default or add a return outside of the switch, but this are still fundamentally hacks to get round a compiler error that shouldn't be an error.

    With regards an enum really just being an int, that's one of .NET's dirty little secrets which is quite embarassing really. Let me declare an enumeration with a finite number of possibilities please and give me a compilation for:

    Decision fred = (Decision)123;
    

    and then throw an exception if somebody tries something like:

    int foo = 123;
    Decision fred = (Decision)foo;
    

    EDIT 2:

    A few people have made comments about what happens when the enum is in a different assembly and how this would result in problems. My point is that this is the behaviour I think should happen. If I change a method signature this will lead to issues, my premis is that changing an enumeration should be this same. I get the impression that a lot of people don't think I understand about enums in .NET. I do I just think that the behaviour is wrong, and I'd hoped that someone might have known about some very obscure feature that would have altered my opinion about .NET enums.