switch, case, and how it sometimes lets you fallthrough

  • Thread starter Thread starter Benny Raymond
  • Start date Start date
B

Benny Raymond

I'm confused as to how fallthrough is limited in switch. For example
the following works:

string switch_test = "a";
switch (switch_test)
{
case "a":
case "b":
case "c":
doSomething(a);
break;
}

but the following does not work:
string switch_test = "a";
switch (switch_test)
{
case "a":
if (<some test>)
break;
case "b":
case "c":
doSomething(a);
}


why is that?
 
A case must either be empty, or exit via a 'jump' statement (break, continue,
return, etc.).

The second example will work if you just include a jump (usually 'break')
after the dosomething(a) line and also ensure that a jump is reached in the
first case (currently, it's only reached if the if test is true).
--
David Anton
www.tangiblesoftwaresolutions.com
Instant C#: VB.NET to C# Converter
Instant VB: C# to VB.NET Converter
Instant C++: C# to C++ Converter
Instant J#: VB.NET to J# Converter
 
This isn't a fix for this, but I wanted to point out that your statement
should also have a 'default' switch to minimize unhandled errors.

like this:

string switch_test = "a";

switch(switch_test)
{
case "a":
{
if (this)
break;
else
do_somthing_nice(switch_test);
break;
}
case "b":
case "c:"
{
do_something_naughty(switch_test);
break;
}
default:
{
whine_about(switch_test);
break;
}
}
 
I know... I just was mainly wondering about how to only break on "a" if
something - otherwise fallthough.
 
On further review, I think the snip I posted might work. Bear in mind though
that before last week, I haven't written a line of C or C++ in about 10
years, and had not even really looked at C#.

In your doesn't work, the first switch/case, "a", is met - which drops it
down to that block. The first instruction on the block is a logical test -
if it's true, it goes to the 'break;' statement. If it's false, you're
winding up back out of the block, into the switch/case block again, and
falling down the code block.

(I haven't had to talk about programming in really technical terms in a long
time, so my vocabulary is a little rusty. I think you see what I'm getting
at, tho.)
 
That's what I would assume... when I hit "a" it goes into the block - if
b, c, or d were there it would go into what's under d because neither b
or c had break statements... However, when I add my "some test" in
there, I get a compile error saying that I cannot fall through - really
quite bizarre since it lets me fall through when I dont have anything
under the case (such as in b and c, or in a, b and c in the first statement)
 
Benny said:
I'm confused as to how fallthrough is limited in switch. For example
the following works:

string switch_test = "a";
switch (switch_test)
{
case "a":
case "b":
case "c":
doSomething(a);
break;
}

but the following does not work:
string switch_test = "a";
switch (switch_test)
{
case "a":
if (<some test>)
break;
case "b":
case "c":
doSomething(a);
}


why is that?

Hi, there are two errors with the code, one being your problem, and the
second being that you're missing a 'break;' after the second set of case
blocks. To workaround your problem, include a 'goto case "b";' statement,
after your if statement:

string switch_test = "a";

switch (switch_test)
{
case "a":
if ( <some-test> )
break;
goto case "b";
case "b":
case "c":
doSomething(a);
break;
}

-- Tom
OrElse what...
 
Fallthrough is diallowed in C#. The design team considered fallthrough
one of the aspects of C / C++ that tended to confuse non-C readers, so
they insisted on a "break" in every case to make the semantics clearer.

Then, the only decision was how to designate cases that should be
executed for multiple values. Should they stay with the C syntax:

case "a";
case "b":
case "c":
doAbcCase();
break;

or go with the Pascal synax:

case "a", "b", "c":
doAbcCase();
break;

They decided on the C syntax.

So, it's not really "fallthrough": it is, rather, a way to indicate
cases that should be executed for more than one value.

As someone who programmed in C for more than 10 years, I can tell you
that the kind of "fallthrough" you're talking about is something that
good C programmers avoided whenever possible. Psychologically it was to
easy to miss the subtle distinction of the missing "break", and so it
was correspondingly more difficult to maintain the code. I never found
an occasion in ten years in which I had to resort to it, so I don't
miss it in C#.
 
Benny Raymond said:
I'm confused as to how fallthrough is limited in switch. For example
the following works:

string switch_test = "a";
switch (switch_test)
{
case "a":
case "b":
case "c":
doSomething(a);
break;
}

but the following does not work:
string switch_test = "a";
switch (switch_test)
{
case "a":
if (<some test>)
break;
case "b":
case "c":
doSomething(a);
}


why is that?

You are not allowed to fall through in a C# switch statement. From the C#
specification:

| The "no fall through" rule prevents a common class of bugs that occur in
| C and C++ when break statements are accidentally omitted. In addition,
| because of this rule, the switch sections of a switch statement can be
| arbitrarily rearranged without affecting the behavior of the statement.

In your last example, you are *not* falling through on case "b"---you are
merely using two labels (case "b" and case "c") in a single section.
 
For the benefit of those looking over our shoulders, so to speak, I offer the
following code - which I wrote and tested quickly in VS2005. It's not very
pretty, and is probably twice as long as it needs to be - C was never my
strong point - and as I pointed out earlier, I'm just now relearing the
language.

Size and efficiency aside, it does the job.

Hope this helps. It's been good practice.

using System;
using System.Collections.Generic;
using System.Text;

namespace casestatementtesting
{
class Program
{
static void Main(string[] args)
{
string thischar = "a";
string nline;


switch(thischar)
{
case "a":
if (sometest(thischar))
{
Console.WriteLine("in switchblock 'a'");
do_something_nice("a");
break;
}
else
{
Console.WriteLine("in switchblock 'a'");
do_something_naughty("a");
break;
}
break;
case "b":
case "c":
Console.WriteLine("Now in Block C");
do_something_nice("a");
do_something_naughty("a");
break;
default:
Console.WriteLine("Now in Default Block");
break;
}
Console.WriteLine("Press Enter to Exit");
nline = Console.ReadLine();
}

static bool do_something_nice(string mystring)
{
Console.WriteLine("You're a handsome devil!");
return true;
}

static bool do_something_naughty(string mystring)
{
Console.WriteLine("Your father was a hampster and your mother
smells of Elderberries!");
return true;
}

static bool sometest(string mychar)
{
if (mychar != "a")
return false;
else
return true;
}
}
}
 
OIC - dunno how I missed that...

Take the code I just posted, remove the code in the 'else block' in case
block 'a', and replace it with

else
goto case "b";

Remove the 'break;' statement directly aboce the 'case "b":' statement, and
you're in business.
 
Bruce said:
Then, the only decision was how to designate cases that should be
executed for multiple values. Should they stay with the C syntax:

case "a";
case "b":
case "c":
doAbcCase();
break;

or go with the Pascal synax:

case "a", "b", "c":
doAbcCase();
break;

They decided on the C syntax.

Which was a bad decision! If they'd gone with Pascal syntax then we
wouldn't have to write that silly "break" at the end of every case. The
Pascal-ish syntax would have been able to omit "break" altogether, but
in about 1% of cases omitting "break" would cause ambiguity with the C
syntax. E.g., in C# we have to write this:

case 1:
break;
case 2:
case 3:
Do23Case();
break;

If C# were to leniently allow us to omit "break", then the compiler
would be confused into thinking that case 1 was intended to call
Do23Case():

case 1:
case 2:
case 3:
Do23Case();

But with Pascal-ish syntax, there would be no ambiguity:

case 1:
case 2, 3:
Do23Case();

Or some such syntax. It's more concise and much less prone to causing
confusion.

I am really looking forward to the day when "Make it look like C"
ceases to be a design requirement for new programming languages.

-- Peter Gummer
 
Regardless of which syntax they had gone with, I doubt that they would
have made "break" optional. They could have done it with the C syntax,
simply changing your example to:

switch (whatever)
{
case 1:
break;
case 2:
case 3:
Do23Case();
default:
DoDefault();
}

That is, requiring the break only to signal a "do-nothing" case.
However, I believe that their concern was people crossing over from
other languages (as has happened in most of the places I've worked). In
particular, C / C++ programmers would misinterpret the above and think
that cases 2 and 3 would fall through to the default and execute
DoDefault() as well.

Requiring the break in all cases makes the statement unambiguous to all
programmers, regardless of their background.

Personally, I like what the C# team have done. I see them as driven by
pragmatic considerations, rather than abstract notions of "purity."
After 20 years in the business, I'm firmly in the pragmatic camp: if
the code is easier to understand and maintain, and its "total cost of
ownership" is lower, then arguments about "elegance" and "purity"
pretty much fall on deaf ears. For me, typing extra "break"s in switch
statements is a small price to pay so that anyone, even newbies, will
be able to understand what I did.
 
Benny Raymond said:
I'm confused as to how fallthrough is limited in switch. For example the
following works:

string switch_test = "a";
switch (switch_test)
{
case "a":
case "b":
case "c":
doSomething(a);
break;
}

but the following does not work:
string switch_test = "a";
switch (switch_test)
{
case "a":
if (<some test>)
break;
case "b":
case "c":
doSomething(a);
}

You have to think of a C# switch differently than a C or C++ swtich. In C,
a switch looks like:

switch (value)
{
label1:
statement-list-1
label2:
statement-list-2
...
}

Any statment list can be empty, and you have fallthrough.

In C#, it's

switch(value)
{
label1A:
label1B:
label1C:
...
statment-list-1;

label2A:
label2B:
label2C:
...
statment-list-2;
}

A list of labels has to have at least one member. There is no fallthrough
from one statement list to the next.
 
ok, so goto spegetti style :) Didn't realize this was the way to move
between cases that you enter and want to fall to somewhere else...

Thanks!
 
Back
Top