When is it appropriate to use a fall-through (classic) switch statement? Is such usage recommended and encouraged or should it be avoided at all costs?
-
Not all languages allow fall-through on switch statements.– OdedOct 25, 2011 at 19:17
-
@Oded, edited, added "classic" word. Not all languages allow fall through, nevertheless I insist it is classic )– shabuncOct 25, 2011 at 19:22
-
3If you are talking about Duffs' device, that's one thing.– OdedOct 25, 2011 at 19:23
-
6@Oded: That's the first thing most people think of, but that's hardly "appropriate." According to this, Duff himself said "This definitely provides an argument in the discussion whether switch should fall through by default, but I'm not sure if the argument is for or against it."– BlueRaja - Danny PflughoeftOct 26, 2011 at 5:32
7 Answers
Here's an example where it would be useful.
public Collection<LogItems> GetAllLogItems(Level level) {
Collection<LogItems> result = new Collection<LogItems>();
switch (level) {
// Note: fall through here is INTENTIONAL
case All:
case Info:
result.Add(GetItemsForLevel(Info));
case Warning:
result.Add(GetItemsForLevel(Warning));
case Error:
result.Add(GetItemsForLevel(Error));
case Critical:
result.Add(GetItemsForLevel(Critical));
case None:
}
return result;
}
This sort of thing (where one case includes the other) is rather rare, I think, which is why some newer languages either don't allow fallover or require special syntax for it.
-
13This example, while interesting, is lacking the requisite all-caps
// INTENTIONAL FALL THROUGH HERE
comment. Oct 26, 2011 at 1:45 -
It's just as easy to write this code with falling through by having the
GetItemsForLevel(Info)
call invokeGetItemsForLevel(Warning)
and so on.– CalebOct 26, 2011 at 14:55 -
-
1@Caleb: In this case, yes. But what if the calls were a little more complicated? It could get a bit hairy, while fall through makes it seem simple. I should note I'm not actually in favour of using fall through - I'm just saying it can be useful in certain situations. Oct 26, 2011 at 15:17
-
Not the best example. There is an ordering of the warning levels. By defining that ordering, this method reduces to a single line of code filtering items when the item level is at least equal to the desired level. Oct 3, 2016 at 18:11
I use them when certain functionality has to be applied for more than one value. For example, say you had an object with a property called operationCode. If the code equals 1, 2, 3 or 4, you want to startOperationX(). If it's 5 or 6, you want to startOperationY() and 7 you startOperationZ(). Why have 7 complete cases with functionality and breaks when you can use fall-throughs?
I think it's completely valid in certain situations, especially if it avoids 100 if-else statements. =)
-
4What you're describing is a
switch
with multiplecase
s tied to the same code. That's different than a fall-through, where execution of onecase
continues into the next one because of the lack of abreak
between them.– BlrflOct 25, 2011 at 20:47 -
4It doesn't really matter, the fall-through is just the lack of a break statement so it "falls through" to the next case.– YatrixOct 25, 2011 at 21:02
-
-
2@Yatrix: I disagree. It is much different to have consecutive cases all executing the exact same block of code than it is to omit a break. One is routine, the other far from it (ime) - and the potential for misreading and unintended control flow is much greater. Oct 25, 2011 at 23:33
-
3@qes yes, it's different, but they are both fall-throughs. He asked when/if it'd be appropriate. I gave an example of when it would. It wasn't meant to be an all-inclusive example. Depending on the language, having statements before a fall-through may not work. I don't think it will with C# stackoverflow.com/questions/174155/…– YatrixOct 26, 2011 at 13:15
I've used them occasionally, I think its always appropriate usage - but only when included with the appropriate comment.
Fall-through cases are perfectly fine. I often find that an enumeration is used in lots of places, and that when you don't need to differentiate some cases it is easier to use fall-through logic.
For example (note the explanatory comments):
public boolean isAvailable(Server server, HealthStatus health) {
switch(health) {
// Equivalent positive cases
case HEALTHY:
case UNDER_LOAD:
return true;
// Equivalent negative cases
case FAULT_REPORTED:
case UNKNOWN:
case CANNOT_COMMUNICATE:
return false;
// Unknown enumeration!
default:
LOG.warn("Unknown enumeration " + health);
return false;
}
}
I find this kind of use perfectly acceptable.
-
Note that there is a big difference between
case X: case Y: doSomething()
andcase X: doSomething(); case Y: doAnotherThing()
. In the first, the intention is quite explicit, while in the second, the fall through may be intentional or not. In my experience, the first example never triggers any warnings in compilers/code analyzers, while the second one does. Personally, I would only call the second example "fall through" - not sure about an "official" definition, though. Jun 12, 2018 at 6:36
It depends on:
- your personal preference
- your employer's coding standards
- the amount of risk involved
The two main problems associated with letting one case fall through to the next are:
It makes your code dependent on the order of the case statements. That's not the case if you never fall through, and it adds a degree of complexity that's often unwelcome.
It's not obvious that the code for one case includes the code for one or more subsequent cases.
Some places explicitly prohibit falling through. If you don't work at such a place, and if you're comfortable with the practice, and if breaking the code in question won't cause any real suffering, then it might not be the worst thing in the world. If you do it, though, be sure to put an attention-grabbing comment nearby to warn those who come later (including the future you).
Here's a quick (admittedly incomplete (no special handling of leap year)) example of fall-through making my life simpler:
function get_julian_day (date) {
int utc_date = date.getUTCDate();
int utc_month = date.getUTCMonth();
int julian_day = 0;
switch (utc_month) {
case 11: julian_day += 30;
case 10: julian_day += 31;
case 9: julian_day += 30;
case 8: julian_day += 31;
case 7: julian_day += 31;
case 6: julian_day += 30;
case 5: julian_day += 31;
case 4: julian_day += 30;
case 3: julian_day += 31;
case 2: julian_day += 28;
case 1: julian_day += 31;
default: break;
}
return julian_day + utc_date;
}
-
5While this is kind of clever, the time you save from doing this is going to be far outweighed by the time it takes for other people to figure out what this does. Also, you could get better performance by removing the fall-through aspect i.e. 31+28+31 is always 90. If you are going to do this, you might as well just put the totals in the cases since there's only 11 to consider. Oct 3, 2016 at 17:23
-
1You're right, my example is definitely contrived :) I will say, however, it's easier to spot a hidden bug when the number of days are spelled out like this instead of pre-computing all the cumulative days for each case of the switch. Oct 3, 2016 at 18:01
-
2I'll grant you that but doing that in a constant declaration would be preferable e.g. FEB_START = 31; MAR_START = FEB_START + 28; etc. not sure what language this is but in many, it would be computed at compile time. The bigger issue here is that it's a little obtuse. Not so much that it's a bad idea, just atypical. Unless there is a really big benefit, it's generally better to stick with common idioms. Oct 3, 2016 at 18:17
-
2Excellent alternative, using constants for start days on this example. Anyway, the point here is that a cumulative sum is a great use for a switch-case fall-through. I agree 100% on using common idioms, but the nature of this question is venturing into the more esoteric features where common idioms are hard to come by. Oct 3, 2016 at 18:37
If I feel a need to go from one case to another (rare, admittedly), I prefer to be very explicit and goto case
, of course, that assumes your language supports it.
Because falling thru is so uncommon, and very easy to overlook while reading code, I feel it is appropriate to be explicit - and a goto, even if it's to a case, should stand out like a sore thumb.
It also helps avoid bugs that may occur when case statements are reordered.