Falling through on switch-cases

K

K Viltersten

Is it possible to make C# fall through
in switch statement?

I was expecting it was possible, why
would we need a "break" otherwise? But
apparently, i get errors when i try to
do that.
 
J

Jon Skeet [C# MVP]

K Viltersten said:
Is it possible to make C# fall through
in switch statement?

No. You can have multiple labels for the same block, e.g.

case 1:
case 2:
// Code

but you can't fall through with code:

case 1:
// Code (no break)
case 2: // Illegal
// Code
I was expecting it was possible, why
would we need a "break" otherwise? But
apparently, i get errors when i try to
do that.

The switch statement isn't brilliantly designed, basically.

See

http://msmvps.com/blogs/jon.skeet/archive/2008/02/05/c-4-part-1-
looking-back-at-the-past.aspx

for a bit more on this and other mistakes (IMO) in C#.
 
J

Jon Skeet [C# MVP]

Rudy Velthuis said:
Surely this one is due to the C/C++-syntax heritage?

Absolutely - it's for historical reasons, but there's no reason it
shouldn't have been fixed anyway. They already changed fall-through
behaviour...
I agree that
braces would have been a better choice. And perhaps ranges, like in
Pascal:

switch (bla)
{
case 1..10:
doThis();
case 11..30:
doThat();
case 31..50:
{
more than one statement;
}
default:
doDefault();
}

Ranges are a possibility, so long as you can always check that a value
falls in only one range. (Easy for built-in numeric types, I guess.) I
don't really mind if/else if/else if too much though.

I suspect there are further ways of transforming it into a more
powerful construct though - Groovy does a reasonable job here. You need
to potentially work out ways of coping with multiple matches, of course
- first wins, execute all, error etc.
 
R

Rudy Velthuis

Jon said:
Ranges are a possibility, so long as you can always check that a
value falls in only one range.

The compiler should simply generate a compile error if the ranges
overlap, like it does in Pascal. Then, only one range can apply. If
you'd allow overlapping ranges, the first match could win.
 
J

Jon Skeet [C# MVP]

Rudy Velthuis said:
The compiler should simply generate a compile error if the ranges
overlap, like it does in Pascal. Then, only one range can apply. If
you'd allow overlapping ranges, the first match could win.

Yes - as I say, it's easy enough to tell for the built-in numeric
types. There may be other types where it's not so obvious though.
 
B

Ben Voigt [C++ MVP]

Jon said:
Yes - as I say, it's easy enough to tell for the built-in numeric
types. There may be other types where it's not so obvious though.

switch is only allowed on built in integral types and string anyway
 
J

Jon Skeet [C# MVP]

Ben Voigt said:
Jon said:
Rudy Velthuis said:
Jon Skeet [C# MVP] wrote:

Ranges are a possibility, so long as you can always check that a
value falls in only one range.

The compiler should simply generate a compile error if the ranges
overlap, like it does in Pascal. Then, only one range can apply. If
you'd allow overlapping ranges, the first match could win.

Yes - as I say, it's easy enough to tell for the built-in numeric
types. There may be other types where it's not so obvious though.

switch is only allowed on built in integral types and string anyway

Well yes, at the moment - but if we're considering possible changes, we
should consider the possibility of a more flexible switch.

It really goes back to what the point of switch is. If it's to use a
lookup to avoid a series of if/else statements for performance reasons,
I'm not sure its place is really deserved in C#.

If its purpose is to find one matching piece of code from a number of
options, there are ways it could be significantly more flexible.
 
B

Ben Voigt [C++ MVP]

Jon said:
No. You can have multiple labels for the same block, e.g.

case 1:
case 2:
// Code

but you can't fall through with code:

case 1:
// Code (no break)
case 2: // Illegal
// Code

This is one of the very few times I've seen Jon wrong. You *can* fall
through, you just have to make it explicit using "goto case" as Paul has
already mentioned. I think that the way C# chose to handle this is very
well designed, you still have all the flexibility of C, but without the
hidden trap.

The compiler message could be a little better though, it should mention your
various options (break, return, throw, goto, goto case, and sometimes
continue, if you happen to be inside a loop).
 
B

Ben Voigt [C++ MVP]

Ben said:
Jon said:
Rudy Velthuis said:
Jon Skeet [C# MVP] wrote:

Ranges are a possibility, so long as you can always check that a
value falls in only one range.

The compiler should simply generate a compile error if the ranges
overlap, like it does in Pascal. Then, only one range can apply. If
you'd allow overlapping ranges, the first match could win.

Yes - as I say, it's easy enough to tell for the built-in numeric
types. There may be other types where it's not so obvious though.

switch is only allowed on built in integral types and string anyway

Err.. that should have said integral types (built in or enum) and string.
Enums are internally represented as one of the built-in integral types.
 
J

Jon Skeet [C# MVP]

Ben Voigt said:
This is one of the very few times I've seen Jon wrong. You *can* fall
through, you just have to make it explicit using "goto case" as Paul has
already mentioned. I think that the way C# chose to handle this is very
well designed, you still have all the flexibility of C, but without the
hidden trap.

The compiler message could be a little better though, it should mention your
various options (break, return, throw, goto, goto case, and sometimes
continue, if you happen to be inside a loop).

I'd argue that that isn't really fall through - it's an explicit
movement. To me, "fall through" implies dropping off the bottom of one
case and into the next implicitly - which isn't allowed.
However, that's sophistry to some extent. You're right that I had
completely forgotten about it - probably never having used it myself.

The sad thing is, I remember having completely forgotten about it
*last* time this came up too. I'm sure that's meant to be the
difference between ignorance and stupidity - ignorance can be cured.

Will try to remember for next time :)
 
B

Ben Voigt [C++ MVP]

Jon said:
I'd argue that that isn't really fall through - it's an explicit
movement. To me, "fall through" implies dropping off the bottom of one
case and into the next implicitly - which isn't allowed.
However, that's sophistry to some extent. You're right that I had
completely forgotten about it - probably never having used it myself.

The sad thing is, I remember having completely forgotten about it
*last* time this came up too. I'm sure that's meant to be the
difference between ignorance and stupidity - ignorance can be cured.

I think it's the compiler error message that is guilty of ignorance here...
time to open an issue with Connect I guess.

It turns out that the MSDN documentation for CS0163 is pretty decent and
would have helped the OP... but still inaccurate.

https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=333623
 
P

Peter Webb

Jon Skeet said:
Ben Voigt said:
Jon said:
Jon Skeet [C# MVP] wrote:

Ranges are a possibility, so long as you can always check that a
value falls in only one range.

The compiler should simply generate a compile error if the ranges
overlap, like it does in Pascal. Then, only one range can apply. If
you'd allow overlapping ranges, the first match could win.

Yes - as I say, it's easy enough to tell for the built-in numeric
types. There may be other types where it's not so obvious though.

switch is only allowed on built in integral types and string anyway

Well yes, at the moment - but if we're considering possible changes, we
should consider the possibility of a more flexible switch.

It really goes back to what the point of switch is. If it's to use a
lookup to avoid a series of if/else statements for performance reasons,
I'm not sure its place is really deserved in C#.

If its purpose is to find one matching piece of code from a number of
options, there are ways it could be significantly more flexible.


As a newbie/amateur, I was disappointed with both case statements and enums
in C#; both are weaker than what I expected. (Almost everything else was way
better than I expected!).

As I understand it, I have to write

..
..
..
thisday = (int) dayofweek.Sunday
..
..
..

switch (thisday);
{case (int) dayofweek.Sunday: dostuff();break;
case (int) dayofweek.Monday: dostuff(); break;
..
..}


What I was hoping for was:

thisday=Sunday;

switch (thisday);
{case Sunday: dostuff(); break;
case Monday: dostuff(); break;}

Or even:

switch(thisday);
{case Sunday: dostuff(); break;
case Monday..Friday: dostuff(); break;
case Saturday: dostuff(); break;}

The continual need to cast back to integers reduces the readability of the
code.

Also, as someone else pointed out, the ++ operator for enums should loop. If
you have a statement:

thisday++;

and thisday was (int)dayofweek.Saturday, it returns what in reality is an
out-of-range enum (as there is no day of the week after Saturday).

What I think would have been more useful is thisday++ being equivalent to

thisday++; if (thisday>(int)dayofweek.Saturday) thisday=(int)
dayofweek.Sunday;

or even better

thisday++; if (thisday>Saturday) thisday=Sunday;

Its probably too late to change this and keep backwards compatibility, so I
think C# is stuck with ++ working for enums as it does for integers.

As somebody else pointed out, Pascal (now 30 years old) had user defined
types which work a little like enums, but in a more readable, maintainable
and intuitive form.
 
J

Jon Skeet [C# MVP]

Peter Webb said:
As a newbie/amateur, I was disappointed with both case statements and enums
in C#; both are weaker than what I expected. (Almost everything else was way
better than I expected!).

I agree with you in general, but it's not quite as bad as you think.
As I understand it, I have to write

.
.
.
thisday = (int) dayofweek.Sunday
.
.
.

switch (thisday);
{case (int) dayofweek.Sunday: dostuff();break;
case (int) dayofweek.Monday: dostuff(); break;
.
.}

It's not as bad as that...
What I was hoping for was:

thisday=Sunday;

switch (thisday);
{case Sunday: dostuff(); break;
case Monday: dostuff(); break;}

.... but it's not as good as that. You can write:

thisday = DayOfWeek.Sunday;

switch (thisday)
{
case DayOfWeek.Sunday:
doStuff();
break;
case DayOfWeek.Monday:
doStuff();
break;
}

The continual need to cast back to integers reduces the readability of the
code.

Fortunately it's not there.
Also, as someone else pointed out, the ++ operator for enums should loop. If
you have a statement:

thisday++;

and thisday was (int)dayofweek.Saturday, it returns what in reality is an
out-of-range enum (as there is no day of the week after Saturday).

Well, it's more complicated than that. Enums don't *always* represent
simple values like that. Sometimes there are gaps, sometimes they're
just the "better known" values in a set (e.g. HTTP status codes). Then
there are flags enums...

It could certainly all be handled better with a lot of careful thought,
but it's not quite as simple as is being made out.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top