143

从我记事起,我就一直避免使用 switch 语句。实际上,我不记得它曾经作为一种可能的做事方式进入我的意识,因为它很早就被钻进我的脑海,它只不过是 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;
   }

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

4

12 回答 12

106

这可能取决于您认为失败的原因。我对这种事情没意见:

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

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

请注意,我使用“邪恶”的C++ FAQ定义

于 2008-10-09T18:18:45.440 回答
61

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

什么时候好 当你想要 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 */ 来表明这是您的意图。

于 2008-10-09T18:17:18.360 回答
24

你听说过达夫的设备吗?这是使用 switch fallthrough 的一个很好的例子。

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

于 2008-10-09T18:16:11.827 回答
21

跌倒确实是一件方便的事情,具体取决于您在做什么。考虑这种安排选项的简洁易懂的方式:

switch ($someoption) {
  case 'a':
  case 'b':
  case 'c':
    // Do something
    break;

  case 'd':
  case 'e':
    // Do something else
    break;
}

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

于 2008-10-09T18:14:40.207 回答
10

几次它可能非常有用,但一般来说,没有失败是期望的行为。应该允许掉线,但不是隐含的。

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

switch (version) {
    case 1:
        // Update some stuff
    case 2:
        // Update more stuff
    case 3:
        // Update even more stuff
    case 4:
        // And so on
}
于 2008-10-09T19:36:00.197 回答
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

于 2009-02-10T00:02:25.350 回答
4

它强大而危险。失败的最大问题是它不明确。例如,如果您遇到频繁编辑的代码,其中有一个带有故障的开关,您怎么知道这是故意的而不是错误?

在我使用它的任何地方,我都会确保对其进行正确评论:

switch($var) {
    case 'first':
        // Fall-through
    case 'second':
        i++;
        break;
 }
于 2008-10-09T18:15:52.030 回答
4

与任何东西一样:如果小心使用,它可以成为一个优雅的工具。

但是,我认为缺点不仅仅是使用它的理由,而且最终不再允许它(C#)。其中的问题是:

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

很好地使用开关/外壳脱落:

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 构造重写,而不会造成任何损失。

我的最后一句话:远离例子中的失败案例标签,除非你在维护使用这种风格并很好理解的遗留代码。

于 2008-10-09T18:17:16.440 回答
3

像在你的第一个例子中那样使用 fall-through 显然是可以的,我不会认为它是一个真正的 fall-through。

第二个例子是危险的并且(如果没有广泛评论)不明显。我教我的学生不要使用这样的结构,除非他们认为值得为它投入一个评论块,它描述了这是一个故意的失败,以及为什么这个解决方案比替代方案更好。这不鼓励草率使用,但在使用它的情况下仍然允许使用它。

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

于 2011-09-10T21:07:43.357 回答
2

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

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

于 2008-10-09T18:17:58.157 回答
1

在某些情况下,使用 fall-throughs 是程序员的一种懒惰行为——他们可以使用一系列 || 例如,语句,而是使用一系列“包罗万象”的开关案例。

话虽如此,当我知道最终无论如何我都将需要这些选项(例如在菜单响应中)但尚未实现所有选项时,我发现它们特别有用。同样,如果您同时对“a”和“A”进行直通,我发现使用 switch 直通比使用复合 if 语句要干净得多。

这可能是风格问题和程序员的想法,但我通常不喜欢以“安全”的名义删除语言的组件——这就是为什么我更倾向于 C 及其变体/后代,而不是说,爪哇。即使我没有“理由”,我也喜欢能够用指针之类的东西到处乱跑。

于 2008-10-09T18:16:54.350 回答
1

只有当它用作代码块的跳转表时才应使用fall-through。如果代码的任何部分在更多案例之前有无条件中断,则所有案例组都应该以这种方式结束。

其他任何东西都是“邪恶的”。

于 2009-02-09T23:07:12.220 回答