在我的第一次代码评审中(不久前),有人告诉我,在所有switch语句中包含一个default子句是一种很好的实践。我最近想起了这个建议,但不记得理由是什么了。现在我听起来很奇怪。

是否有一个合理的理由总是包含默认语句? 这是语言依赖吗?我不记得当时我在用什么语言了——也许这适用于一些语言而不是其他语言?


当前回答

一些(过时的)指南是这么说的,比如MISRA C:

最后一个默认子句的要求是防御性编程。该条款应采取适当的行动,或包含关于为什么不采取行动的适当评论。

这个建议已经过时了,因为它不是基于目前的相关标准。明显的疏漏是哈兰·卡斯勒说的

省略默认大小写允许编译器在看到未处理的大小写时选择性地发出警告或失败。静态可验证性毕竟比任何动态检查都要好,因此当您也需要动态检查时,这种牺牲是不值得的。

正如Harlan还演示的那样,在切换之后可以重新创建与默认情况相同的功能。当每个情况都是早期返回时,这是微不足道的。

从广义上讲,动态检查的典型需求是输入处理。如果一个值来自程序控制之外,它就不可信。

这也是Misra采取极端防御性编程立场的地方,即只要一个无效值在物理上是可表示的,就必须检查它,无论程序是否可证明是正确的。如果软件需要在出现硬件错误时尽可能地可靠,这是有意义的。但正如Ophir Yoktan所说,大多数软件最好不要“处理”错误。后一种做法有时被称为进攻性编程。

其他回答

No.

如果没有默认操作,那么上下文就很重要了。如果你只关心几个价值怎么办?

以读取游戏按键为例

switch(a)
{
   case 'w':
     // Move Up
     break;
   case 's':
     // Move Down
     break;
   case 'a':
     // Move Left
     break;
   case 'd':
     // Move Right
     break;
}

添加:

default: // Do nothing

只是浪费时间,毫无理由地增加了代码的复杂性。

在某些情况下,没有默认情况实际上是有益的。

If your switch cases are enums values, by not having a default case, you can get a compiler warning if you are missing any cases. That way, if new enum values are added in the future and you forget to add cases for these values in the switch, you can find out about the problem at compile time. You should still make sure the code takes appropriate action for unhandled values, in case an invalid value was cast to the enum type. So this may work best for simple cases where you can return within the enum case rather than break.

enum SomeEnum
{
    ENUM_1,
    ENUM_2,
    // More ENUM values may be added in future
};

int foo(SomeEnum value)
{
    switch (value)
    {
    case ENUM_1:
        return 1;
    case ENUM_2:
        return 2;
    }
    // handle invalid values here
    return 0;
 }

我总是会使用默认子句,无论您使用哪种语言。

事情会出错,也确实会出错。价值观不会是你所期望的,等等。

不想包含默认子句意味着您确信自己知道可能的值集。如果你相信你知道这个可能值的集合,那么如果这个值在这个可能值的集合之外,你会想要知道它-这当然是一个错误。

这就是为什么你应该总是使用默认子句并抛出错误的原因,例如在Java中:

switch (myVar) {
   case 1: ......; break;
   case 2: ......; break;
   default: throw new RuntimeException("unreachable");
}

除了“unreachable”字符串之外,没有理由包含更多信息;如果它真的发生了,无论如何您都需要查看源代码和变量的值等,异常堆栈跟踪将包括该行号,因此不需要浪费时间在异常消息中写入更多文本。

我会说这取决于语言,但在C中,如果你要打开一个枚举类型并处理每个可能的值,最好不要包含默认大小写。这样,如果您稍后添加了一个额外的enum标记,并且忘记将其添加到交换机中,称职的编译器将会就缺少的大小写向您发出警告。

enum所使用的开关可能不需要默认大小写。当switch包含所有值时,默认情况将永远不会执行。所以在这种情况下,这是不必要的。