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

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


当前回答

开关情况几乎总是有一个默认情况。

使用默认值的原因

1.“捕捉”一个意外值

switch(type)
{
    case 1:
        //something
    case 2:
        //something else
    default:
        // unknown type! based on the language,
        // there should probably be some error-handling
        // here, maybe an exception
}

2. 处理“默认”操作,其中的case是针对特殊行为的。

您在菜单驱动程序和bash shell脚本中经常看到这种情况。当变量声明在switch-case之外但没有初始化时,您也可能会看到这种情况,并且每个case初始化它为不同的东西。这里的默认值也需要初始化它,以便访问变量的代码不会引发错误。

3.向阅读您的代码的人展示您已经覆盖了这种情况。

variable = (variable == "value") ? 1 : 2;
switch(variable)
{
    case 1:
        // something
    case 2:
        // something else
    default:
        // will NOT execute because of the line preceding the switch.
}

这是一个过于简化的例子,但关键是阅读代码的人不应该怀疑为什么变量不能是1或2以外的东西。


我能想到的唯一不使用默认值的情况是,当开关正在检查一些非常明显的东西时,其他选项都可以愉快地忽略

switch(keystroke)
{
    case 'w':
        // move up
    case 'a':
        // move left
    case 's':
        // move down
    case 'd':
        // move right
    // no default really required here
}

其他回答

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

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

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

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

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

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

如果你知道switch语句只有一组严格定义的标签或值,那么就这样做,以覆盖基础,这样你总是会得到有效的结果。只需将默认值放在标签之上,从编程/逻辑上来说,它将是其他值的最佳处理程序。

switch(ResponseValue)
{
    default:
    case No:
        return false;
    case Yes;
        return true;
}

这取决于switch在特定语言中的工作方式,但是在大多数语言中,当没有匹配到case时,switch语句的执行就会毫无警告地结束。想象一下,你期望得到一组值,并在switch中处理它们,然而你在输入中得到了另一个值。什么都没发生,你也不知道什么都没发生。如果您发现了违约情况,您就会知道有问题。

在不真正需要的时候使用默认子句是防御性编程 这通常导致代码过于复杂,因为有太多的错误处理代码。 这种错误处理和检测代码损害了代码的可读性,使维护更加困难,并最终导致比解决更多的错误。

因此,我认为如果不应该达到默认值—您不必添加它。

注意,“不应该达到”意味着如果达到了,这是软件中的一个错误——你确实需要测试可能包含不需要的值的值,因为用户输入等等。

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

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