交换声明失败... 应该被允许吗?

从我记事起,我就一直避免使用 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 语句中执行所有操作。我已经看到这个设计,我只是不知道我是否同意开关语句应该这样使用。我认为第一个代码示例非常有用和安全。后者似乎有点危险。

184343 次浏览

考虑一下这个简单易懂的方法来安排你的选择:

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


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

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

它既强大又危险。失败最大的问题是它不够明确。例如,如果您遇到经常编辑的代码,其中有一个带有缺陷的开关,您如何知道这是故意的,而不是一个 bug?

无论我在哪里使用它,我都会确保它得到正确的注释:

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

你听说过 达夫的装置吗? 这是一个很好的使用 switch Fallthrough 的例子。

它是一个可以被使用和滥用的特性,就像几乎所有的语言特性一样。

在某些情况下,使用掉链接是程序员的一种懒惰行为——例如,他们可以使用一系列 | | 语句,而不是使用一系列“全部”开关案例。

也就是说,我发现它们特别有用,因为我知道 最终无论如何都需要这些选项(例如在菜单响应中) ,但是还没有实现所有的选项。同样地,如果您同时使用‘ a’和‘ A’的 fall-through,我发现使用 switch fall-through 比使用复合 if 语句要简单得多。

这可能是一个风格和程序员如何思考的问题,但我通常不喜欢以“安全”的名义删除语言的组件——这就是为什么我更倾向于 C 及其变体/后代,而不是 Java。我喜欢在指针之类的东西上胡闹,即使我没有“理由”这么做。

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

然而,我认为这个缺点比 没有使用它更合理,最终不再允许它(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 构造进行重写,而不会造成任何损失。

我的最后一句话: 远离像 很糟糕示例中那样的落后的 case 标签,除非您正在维护使用并充分理解这种风格的遗留代码。

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

什么时候合适? 如果你想要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 */来表明你的意图。

我不希望我的 switch语句失败——它太容易出错,而且很难阅读。唯一的例外是多个 case语句都执行相同的 没错操作。

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

这可能取决于你对失败的看法。我对这类事情没有意见:

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 + + 常见问题解答定义

有几次它可能非常有用,但是一般来说,没有失败是需要的行为。应该允许失败,但不能含蓄。

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

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

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

其他的都是“邪恶”。

我喜欢在开关中使用不同的回退语法,比如,errr. 。

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

注意: 如果您使用标志声明枚举上的所有情况,那么对枚举来说这是可能的,对吗?这听起来也没有那么糟糕; 这些案例可能(应该?)很可能已经成为你的一部分了。

对于使用扩展方法的流畅接口来说,这可能是一个很好的例子(没有双关语的意思) ,比如,errr..。

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

像您的第一个示例那样使用 fall-through 显然是可以的,而且我不会认为它是一个真正的 fall-through。

第二个示例是危险的,而且(如果没有广泛注释的话)不明显。我教导我的学生不要使用这样的结构 除非,他们认为值得花费精力在它上面,它描述了这是一个有意的失败,以及为什么这个解决方案比其他方案更好。这阻止了草率的使用,但是在它被用于优势的情况下仍然允许使用它。

这或多或少与我们在太空项目中的做法类似,当有人想要违反编码标准时: 他们必须申请豁免(我被要求就裁决提出建议)。