4

This is not specifically a C# question but it is the C# version of switch that makes me ask the question.

Why do I have to explicitly state that I do not want to fall through from one case statement to the next instead of instead indicating when I DO want to fall through?

In C#, the compiler will not let you fall through from one case statement to the next (unless the case statement is empty) and yet it forces you to put an explicit break at the end of every one. The vast majority of switch statements that I write have no fall through.

int x = 4;
string result;

switch (x)
{
case 1:
   result = "One";
   break;
case 2:
   result = "A couple";
   break;
case 3:
case 4:
   result = "Three or four";
   break;
/*
case 5:
   result = "Five";   // Here be an error! A common typo for me!!!
*/
default:
   result = "Five or more";
   break;
}

The above example is contrived to show the possibilities. The number of times I actually fall-through (like between 3 and 4 above) is minimal. If I remove the comments from case 5 above, the C# compiler will catch it as an error. Why do I have to fix it manually if the compiler knows full well what I am trying to do?

Wouldn't the following be better?

int x = 4;
string result;

switch (x)
{
case 1:
   result = "One";
case 2:
   result = "A couple";
case 3:
   continue;   // or even goto 4
case 4:
   result = "Three or four";
/*
case 5:
   result = "Five";   // This would not longer be an error!! Hooray!
*/
default:
   result = "Five or more";
}

Let me be clear that I am not advocating fall-through here. There is an implied break at the end of every case statement. The two examples are meant to be functionally equivalent.

The compiler could still yell at me if I forgot the continue statement in case 3. This would not happen very often though and I would be happy for the help. In fact, the chance that I just missed filling in the skeleton code is pretty high in this case. Useful. Telling me that I forgot a break statement is never helpful. It is just more busy-work to keep the compiler happy.

It seems like this is probably just a hold-over from C (which does allow you to fall through even when you have code in the case statement). Is there a deeper reason that I do not see?

I was looking at some compiler code just this morning and there were hundreds of case statements in a switch that did nothing but return op-codes. Every one of them had a break statement. So much typing and clutter...

What do people think? Why is the second option not a better default?

Justin
  • 8,853
  • 4
  • 42
  • 42
  • 2
    I agree. It seems like the syntax should make the default easy and create a keyword for the edge case. Maybe this was an attempt to keep things looking C'ish? Just a guess. – Ed S. Aug 09 '11 at 00:44
  • 2
    Because C# was designed by skilled programmers that knew very well what was wrong with the C version of the switch statement. Forgetting to write *break;* is an incredibly common bug. Use the, drumroll, *goto* keyword. – Hans Passant Aug 09 '11 at 00:47
  • 2
    @Hans - Oh, I agree. C# got it right in not allowing fall-through. But, since it does not allow fall-through, why do I have to tell it NOT to fall-through? It isn't going to do it either way. My instructions do not actually add any new information that the compiler is not already enforcing itself. I show the use of 'goto' above in just the way I think you mean. – Justin Aug 09 '11 at 00:54
  • Well, it also had to be recognizable back 10 years ago. If that wouldn't have been a constraint then they would have adopted the vb version. – Hans Passant Aug 09 '11 at 00:57

3 Answers3

6

The rationale for C# switch statements is that C's behavior is that cases fall through unless you explicitly exit with a statement like break, return, or goto. This means that everybody who is familiar with C (which is probably most C# programmers) will be expecting the C# switch syntax to mean approximately the same thing. If the C# designers made cases not fall through, code would do the exact opposite of what users will expect!

On the other hand, it's a common source of bugs for people to forget the break statement at the end of a case and have control fall through where it wasn't supposed to. A less common problem is that people will re-order the cases such that the one which used to be at the end (and not need a break) is no longer at the end (and falling through unexpectedly).

In other words, making cases not fall through would cause bugs from people who are expecting it to fall through, but not requiring the explicit termination of the case would cause bugs from people who forgot to put in a break. Thus, cases can't fall through, but a break is still required.

Gabe
  • 84,912
  • 12
  • 139
  • 238
  • Thanks Gabe. The Principal of Least Astonishment is a pretty valid answer to my question. One thing though, C# already does not fall through (unless the case statement is empty). You cannot forget to add 'break' and get a bug in C#. Not including 'break' is a compile-time error. So, C# 'switch' actually does not behave at all as C programmers would expect. That said, once the code is actually written to build successfully, the resulting code does mimic what C (and C++) programmers would expect from similar C code as you say. – Justin Aug 09 '11 at 04:50
  • @Justin: Good point. With respect to `switch`, the *code* behaves just as a C programmer expects, while the *compiler* behaves slightly differently (requiring a break for a non-empty case). – Gabe Aug 09 '11 at 14:34
1

switch statements really shine in the case when things like this happen (to replace ifs)

switch (my_var) {
    case 1:
    case 2:
    case 3:
    case 4:
        do_something();
        break;
    case 5:
    case 6:
    case 7:
    case 8:
        do_something else();
        break;
}

This replaces heavily either nested ifs, or giant if statements with ands in them.

The other main reason is that it is kind of brought back from C. Case statements look almost exactly like the assembly that would be generated by the compiler for them (in some cases). Not really too sure why they went with this design at first but I really see no issue as the same number of if's might also seem cluttered (not always though).

C# probably see's the missing break as an error (common one at that) and probably that's the reason for flagging it. It usually only does this with the last one that falls through to the default statement and the default statement itself has to have a break (because you can always put the default at the beginning). C# tries to be strict in this sense so as to avoid some simple bugs that can be caused by simply forgetting to type break at the end of every case (or some case) and having it fall through execution.

Jesus Ramos
  • 22,940
  • 10
  • 58
  • 88
  • Thanks. I guess my own experience is that switch statements like the above are not the norm. Also, if you look at your example, you could do the exact same thing with a single if/else statement. Since switch statements are just sugar for if/else trees, why not tailor them to the ones with the most branches? – Justin Aug 09 '11 at 00:52
  • It's much easier to read sometimes, also for if statements that start to span multiple lines it can get confusing. Also my example is not really what I wanted it to be but just something to get the point across. (I built an opcode disassembler and in this case it's so much easier to us switch than multi line if's) – Jesus Ramos Aug 09 '11 at 00:54
0

The reason that C and C++ have implicit fall-through is backward compatibility with a tremendous amount of code that depends on it, even though rarely.

The reason that C# doesn't have implicit fall-through is that it's a bug in most cases. The reason that C# doesn't have implicit break is that it would be a surprise to C, C++, and Java programmers. And it wouldn't even generate a warning? Silent unexpected failures are very bad.

Requiring the behavior to be explicit is the safest option, and that's the route C# took. (and I wish that C++ would develop an explicit syntax for fall-through, so that compilers could warn about implicit fall-through)

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • Its actually quite common! Duff devices would not work without fall through, a lot of finite state machines use fall through (this is particularly handy when the condition signaling the end of one state doubles as the start of a new state.) – James Anderson Aug 09 '11 at 02:16
  • @James: And that's still a very small fraction of all switch statements. I believe what I said is exactly correct: A tremendous amount of code needs it, but it's a small fraction of all usage (rare). – Ben Voigt Aug 09 '11 at 02:52