适当使用穿透开关语句


14

什么时候使用穿透(经典)switch语句?是否建议并鼓励这种用法,还是应不惜一切代价避免使用?


并非所有语言都允许switch语句掉线。
Oded

@Oded,编辑,添加“经典”一词。并不是所有的语言都允许失败,不过我坚持认为这是经典)
shabunc

3
如果您在谈论Duffs的设备,那是一回事。
Oded

6
@Oded:这是大多数人想到的第一件事,但这几乎不是“适当的”。根据,达夫自己说“这绝对提供了讨论的论点开关是否应该被默认告吹,但我不知道,如果该参数为支持或反对它。”
BlueRaja-Danny Pflughoeft

Answers:


15

这是一个有用的示例。

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;
}

我认为这种事情(其中一种情况包括另一种情况)非常少见,这就是为什么一些较新的语言要么不允许失败,要么需要特殊语法的原因。


13
这个例子虽然有趣,但缺少必要的全大写// INTENTIONAL FALL THROUGH HERE注释。
罗素·博罗戈夫

通过GetItemsForLevel(Info)调用调用GetItemsForLevel(Warning)等等来编写此代码很容易。
Caleb,

@RussellBorogove:完全正确。
配置工具,

@Caleb:在这种情况下,是的。但是,如果通话更加复杂怎么办?它可能会变得有些毛茸茸,而掉线会使它看起来很简单。我应该指出,我实际上并不赞成使用直通广告-我只是说它在某些情况下很有用。
Configurator

不是最好的例子。警告等级有序。通过定义该顺序,当项目级别至少等于所需级别时,此方法减少到一行代码来过滤项目。
凯文·克莱恩

8

当某些功能必须应用多个值时,我会使用它们。例如,假设您有一个对象,其对象名为operationCode。如果代码等于1、2、3或4,则要启动OperationX()。如果是5或6,则要startOperationY(),要7,则要startOperationZ()。当您可以使用穿透功能时,为什么要有7个完整的功能案例和中断案例?

我认为这在某些情况下是完全有效的,尤其是避免使用100条if-else语句时。=)


4
您要描述的是一个switch与多个case绑定到同一代码的。这与失败(fall-through)不同,后者case由于在break两者之间缺少a ,因此继续执行到下一个。
Blrfl 2011年

3
没关系,失败只是缺少break语句,因此它“陷入”了下一个情况。
Yatrix 2011年

我会在您的答案中重新措词以反映这一点。
Blrfl 2011年

2
@Yatrix:我不同意。有连续的情况都执行完全相同的代码块与省略中断有很大不同。一个是例行程序,另一个是例行程序(ime),它可能会产生误读和意外控制流的可能性。
2011年

3
@qes是的,这是不同的,但是它们都是失败的。他问何时/是否合适。我举了一个例子。这并不意味着要成为一个包罗万象的例子。根据语言的不同,在插入之前进行语句可能不起作用。我认为C#stackoverflow.com
questions/174155/…

8

我偶尔使用它们,我认为它总是合适的用法-但仅当包含在适当的注释中时才使用。


7

掉线的情况很好。我经常发现在很多地方都使用了枚举,并且当您不需要区分某些情况时,使用穿透逻辑会更容易。

例如(请注意解释性注释):

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;
  }
}

我发现这种用法完全可以接受。


请注意,case X: case Y: doSomething()和之间有很大的区别case X: doSomething(); case Y: doAnotherThing()。在第一个中,意图是很明确的,而在第二个中,失败可能是有意的,也可能不是有意的。以我的经验,第一个示例从不会在编译器/代码分析器中触发任何警告,而第二个示例会触发。就个人而言,我仅将第二个示例称为“失败”-但是不确定“官方”定义。
延斯·班曼

6

这取决于:

  • 您的个人喜好
  • 您雇主的编码标准
  • 涉及的风险量

与让一种情况进入另一种情况有关的两个主要问题是:

  1. 它使您的代码取决于case语句的顺序。如果您从不失败,那就不是这种情况,它增加了一定程度的复杂性,这通常是不受欢迎的。

  2. 一个案例的代码是否包含一个或多个后续案例的代码,这一点并不明显。

有些地方明确禁止摔倒。如果您不在这样的地方工作,并且对练习感到满意,并且如果破坏所讨论的代码不会造成任何实际的痛苦,那么这可能不是世界上最糟糕的事情。不过,如果您这样做了,请务必在附近添加引人注目的评论,以警告那些后来者(包括将来的您)。


4

这是一个失败的快速(公认的不完整(no年没有特殊处理))示例,它使我的生活更简单:

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;
}

5
尽管这很聪明,但是您节省下来的时间将远远超过其他人弄清楚该功能所花费的时间。另外,通过消除掉落方面可以得到更好的性能,即31 + 28 + 31始终为90。如果要执行此操作,最好只考虑总数,因为只考虑了11个。
JimmyJames '16

没错,我的示例绝对是人为的:)但是,我会说,这样拼写出的天数会更容易发现隐藏的错误,而不是为每种情况的切换预先计算所有累积的天数。
AaronDanielson '16

2
我会同意的,但是最好在常量声明中这样做,例如FEB_START = 31; MAR_START = FEB_START + 28; 等不确定这是什么语言,但是在很多情况下,它将在编译时进行计算。这里更大的问题是它有点钝。与其说这是一个坏主意,不如说是非典型的。除非有真正的大好处,否则通常最好还是坚持使用惯用语。
JimmyJames '16

1
极好的选择,在此示例中,开始日期使用常量。无论如何,这里的要点是,累积总和对于切换案例失败很有用。我同意100%使用常见习语,但是这个问题的本质是冒险进入难以理解的常见习俗中。
AaronDanielson

3

如果我觉得有必要从一种情况转到另一种情况(诚然,很少见),我宁愿非常明确goto case,当然,前提是您的语言支持。

因为直通非常罕见,并且在阅读代码时很容易被忽略,所以我觉得应该露骨-转到goto,即使是在这种情况下,也应该像酸痛的拇指一样突出。

它还有助于避免对case语句重新排序时可能发生的错误。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.