Switch Statement Fallthrough ...是否允许?

时间:2008-10-09 18:11:09

标签: coding-style switch-statement

只要我记得我已经避免使用switch语句。实际上,我不记得它曾经进入我的意识,作为一种可能的做事方式,因为它很早就钻进了我的脑袋,它只不过是转换语句中的一个错误。但是,今天我遇到了一些设计使用它的代码,这让我立刻想知道社区中的每个人都想到了关于switch语句的问题。

编程语言是否应该明确不允许(就像C#那样,虽然它提供了一种解决方法),还是任何语言的功能都足以留在程序员手中?

修改 我对暴露的意思不够具体。我经常使用这种类型:

    switch(m_loadAnimSubCt){
        case 0: 
        case 1: 
            // Do something
            break;
        case 2:
        case 3:
        case 4:
            // Do something
            break;
   }

但是,我担心这样的事情。

   switch(m_loadAnimSubCt){
        case 0: 
        case 1: 
            // Do something but fall through to the other cases 
            // after doing it.
        case 2:
        case 3:
        case 4:
            // Do something else.
            break;
   }

这种方式只要大小写为0,1就会在switch语句中执行所有操作。我已经通过设计看到了这一点,我只是不知道我是否同意以这种方式使用switch语句。我认为第一个代码示例非常有用且安全。第二种似乎很危险。

12 个答案:

答案 0 :(得分:75)

这可能取决于您认为的漏洞。我很喜欢这种事情:

switch (value)
{
  case 0:
    result = ZERO_DIGIT;
    break;

  case 1:
  case 3:
  case 5:
  case 7:
  case 9:
     result = ODD_DIGIT;
     break;

  case 2:
  case 4:
  case 6:
  case 8:
     result = EVEN_DIGIT;
     break;
}

但是如果你有一个案例标签,后面跟着另一个案例标签的代码,我几乎总是认为这是邪恶的。也许将公共代码移动到一个函数并从两个地方调用将是一个更好的主意。

请注意,我使用了C++ FAQ

"evil"定义

答案 1 :(得分:49)

这是一把双刃剑。有时非常有用,通常很危险。

什么时候好?当你想要10个案例时,所有处理方式都相同......

switch (c) {
  case 1:
  case 2:
            ... do some of the work ...
            /* FALLTHROUGH */
  case 17:
            ... do something ...
            break;
  case 5:
  case 43:
            ... do something else ...
            break;
}

我喜欢的一条规则是,如果你做任何想要排除休息的事情,你需要一个明确的评论/ * FALLTHROUGH * /来表明你的意图。

答案 2 :(得分:20)

根据你正在做的事情,堕落真的很方便。考虑这种安排选项的简洁易懂的方法:

switch ($someoption) {
  case 'a':
  case 'b':
  case 'c':
    // do something
    break;
  case 'd':
  case 'e':
    // do something else
    break;
}

想象一下用if / else做这件事。这将是一团糟。

答案 3 :(得分:19)

您听说过Duff's device吗?这是使用switch fallthrough的一个很好的例子。

这是一个可以使用的功能,它可以像几乎所有语言功能一样被滥用。

答案 4 :(得分:9)

它可能非常有用几次,但一般来说,没有穿透是理想的行为。应该允许通过,但不是隐含的。

更新某些数据的旧版本的示例:

switch (version) {
    case 1:
        // update some stuff
    case 2:
        // update more stuff
    case 3:
        // update even more stuff
    case 4:
        // and so on
}

答案 5 :(得分:6)

我喜欢在交换机中使用不同的回退语法,例如,errr ..

switch(myParam)
{
  case 0 or 1 or 2:
    // do something;
    break;
  case 3 or 4:
    // do something else;
    break;
}

注意:如果你使用标志声明你的枚举上的所有案例,那么枚举就已经可以了吗?听起来也不是那么糟糕,案件可能(应该?)很好地成为你的枚举的一部分。

对于使用扩展方法的流畅界面,这可能是一个很好的案例(没有双关语)?有点像错误...

int value = 10;
value.Switch()
  .Case(() => { /* do something; */ }, new {0, 1, 2})
  .Case(() => { /* do something else */ } new {3, 4})
  .Default(() => { /* do the default case; */ });

虽然这可能更不易读:P

答案 6 :(得分:5)

与任何事情一样:如果小心使用,它可以是一个优雅的工具。

然而,我认为缺点不仅仅是不使用它,最后不再允许它(C#)。问题包括:

  • 很容易“忘记”休息
  • 对于代码维护者来说,并不总是显而易见的是省略的中断是故意的

善用开关/案例:

switch (x)
{
case 1:
case 2:
case 3:
 do something
 break;
}

BAAAAAD使用开关/案例:

switch (x)
{
case 1:
    some code
case 2:
    some more code
case 3:
    even more code
    break;
}

在我看来,这可以使用if / else结构重写,完全没有任何损失。

我的最后一句话:远离BAD示例中的堕落案例标签,除非您维护使用此样式且易于理解的旧代码。

答案 7 :(得分:4)

强大而危险。堕落的最大问题是它不明确。例如,如果您遇到经常编辑的代码,其中有一个带有漏洞的开关,您如何知道这是故意的而不是错误?

我使用它的任何地方,我都确保它已正确评论:

switch($var) {
    case 'first':
        // fall-through
    case 'second':
        i++;
        break;
 }

答案 8 :(得分:3)

在你的第一个例子中使用堕落显然是可以的,我不认为这是真正的堕落。

第二个例子是危险的(如果没有广泛评论)是不明显的。我教我的学生不要使用这样的结构,除非他们认为值得为它设一个评论块,这说明这是一个故意的落实,以及为什么这个解决方案比替代方案更好。这样可以避免草率使用,但在使用它的情况下仍允许使用它。

这或多或少等同于我们在太空项目中所做的事情,当有人想要违反编码标准时:他们必须申请免除(我被要求就裁决提出建议)。

答案 9 :(得分:2)

我不喜欢我的switch陈述 - 它太容易出错并且难以阅读。唯一的例外是多个case语句都完全同样的事情。

如果有一些公共代码需要使用switch语句的多个分支,我将其提取到一个可以在任何分支中调用的单独的公共函数中。

答案 10 :(得分:1)

在某些情况下,使用堕落是程序员的一种懒惰行为 - 他们可以使用一系列||例如,语句,而是使用一系列'全能'切换案例。

话虽如此,当我知道最终我仍然需要选项时(例如在菜单响应中),我发现它们特别有用,但还没有实施了所有选择。同样地,如果你正在为'a'和'A'做一个翻版,我发现使用切换掉落比使用复合if语句更清晰。

这可能是一种风格问题以及程序员的想法,但我并不喜欢以“安全”的名义删除一种语言的组成部分 - 这就是为什么我倾向于C及其变体/后代而不是比方说,Java。即使我没有“理由”,我也喜欢能够用指针等进行喋喋不休。

答案 11 :(得分:1)

只有当它被用作跳转表到代码块时,才应该使用 think。如果代码的任何部分在更多情况下具有无条件中断,则所有案例组都应以此方式结束。其他任何事情都是“邪恶的”。