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

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


当前回答

我不同意上面Vanwaril投票最多的回答。

任何代码都会增加复杂性。此外,还必须为此进行测试和文档编制。所以用更少的代码编程总是好的。我的观点是,我对非穷举switch语句使用default子句,而对穷举switch语句不使用default子句。为了确保我做对了,我使用了静态代码分析工具。让我们来详细了解一下:

Nonexhaustive switch statements: Those should always have a default value. As the name suggests those are statements which do not cover all possible values. This also might not be possible, e.g. a switch statement on an integer value or on a String. Here I would like to use the example of Vanwaril (It should be mentioned that I think he used this example to make a wrong suggestion. I use it here to state the opposite --> Use a default statement): switch(keystroke) { case 'w': // move up case 'a': // move left case 's': // move down case 'd': // move right default: // cover all other values of the non-exhaustive switch statement } The player could press any other key. Then we could not do anything (this can be shown in the code just by adding a comment to the default case) or it should for example print something on the screen. This case is relevant as it may happen. Exhaustive switch statements: Those switch statements cover all possible values, e.g. a switch statement on an enumeration of grade system types. When developing code the first time it is easy to cover all values. However, as we are humans there is a small chance to forget some. Additionally if you add an enum value later such that all switch statements have to be adapted to make them exhaustive again opens the path to error hell. The simple solution is a static code analysis tool. The tool should check all switch statements and check if they are exhaustive or if they have a default value. Here an example for an exhaustive switch statement. First we need an enum: public enum GradeSystemType {System1To6, SystemAToD, System0To100} Then we need a variable of this enum like GradeSystemType type = .... An exhaustive switch statement would then look like this: switch(type) { case GradeSystemType.System1To6: // do something case GradeSystemType.SystemAToD: // do something case GradeSystemType.System0To100: // do something } So if we extend the GradeSystemType by for example System1To3 the static code analysis tool should detect that there is no default clause and the switch statement is not exhaustive so we are save.

还有一件事。如果我们总是使用默认子句,那么静态代码分析工具可能无法检测穷尽性或非穷尽性switch语句,因为它总是检测到默认子句。这是非常糟糕的,因为如果我们将枚举扩展为另一个值,并且忘记将其添加到一个switch语句中,我们将不会得到通知。

其他回答

我相信这是特定于语言的,对于c++来说是枚举类类型的一个次要问题。这似乎比传统的C enum更安全。但

如果你看一下std::byte的实现,它是这样的:

enum class byte : unsigned char {} ;

来源:https://en.cppreference.com/w/cpp/language/enum

再想想这个:

否则,如果T是一个枚举类型,它的作用域是或 具有固定底层类型的无作用域,如果带括号的init-list具有 只有一个初始化式,如果从初始化式转换为 基础类型是非窄化的,如果初始化为 -直接列表初始化,然后使用 将初始化式转换为其基础类型的结果。 (因为c++ 17)

来源:https://en.cppreference.com/w/cpp/language/list_initialization

这是一个枚举类的例子,表示的值不是定义的枚举值。由于这个原因,您不能完全信任枚举。根据应用的不同,这一点可能很重要。

然而,我真的很喜欢@Harlan Kassler在他的帖子里说的话,我自己也会在某些情况下开始使用这个策略。

这是一个不安全枚举类的例子:

enum class Numbers : unsigned
{
    One = 1u,
    Two = 2u
};

int main()
{
    Numbers zero{ 0u };
    return 0;
}

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

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

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

“switch”语句应该总是包含一个默认子句吗?不。它通常应该包含一个默认值。

包含默认子句只有在它需要做某些事情时才有意义,比如断言错误条件或提供默认行为。包括一个“仅仅因为”是狂热的节目,没有任何价值。这相当于说所有的“if”语句都应该包含一个“else”。

下面是一个毫无意义的小例子:

void PrintSign(int i)
{
    switch (Math.Sign(i))
    {
    case 1:
        Console.Write("positive ");
        break;
    case -1:
        Console.Write("negative ");
        break;
    default: // useless
    }
    Console.Write("integer");
}

这相当于:

void PrintSign(int i)
{
    int sgn = Math.Sign(i);
    if (sgn == 1)
        Console.Write("positive ");
    else if (sgn == -1)
        Console.Write("negative ");
    else // also useless
    {
    }
    Console.Write("integer");
}

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