发布于 2026-01-06 12 阅读
0

Why switch is better than if-else Better Semantics Less room for errors and nonsense Reduced duplication Better performance Language problems

为什么 switch 比 if-else 更好

更好的语义

容错空间更小,胡闹的可能性也更低

减少重复

性能更佳

语言问题

Ben 的帖子中,他质疑 switch 语句是否比 if-else 链更简洁。我认为 switch 语句更简洁,因为它们能更好地表达代码语义,减少出错的可能性,降低代码重复,并有可能提升性能。

更好的语义

switch 语句与 if-else 语句链表达的含义不同。switch 语句表示将输入值映射到一段代码。



switch( expr ) {
    case value_0:
        ...

    case value_1:
        ...
}


Enter fullscreen mode Exit fullscreen mode

一眼就能看出,我们试图涵盖所有可能的值expr。阅读这段代码的人可以很快明白它应该做什么。

但使用等效的 if-else 链时,这一点就不那么清晰了:



if( expr == value_0) {
    ...
} else if( expr == value_1) {
    ...
}


Enter fullscreen mode Exit fullscreen mode

我们在这里并不确定我们指的是涵盖所有可能的值,还是仅仅指这些特定的值。

switch许多编译器会在语句缺少条件时发出警告。在 C++ 中,这可能表现case为枚举类型缺少条件语句。在 Rust 中,等效的match结构覆盖范围更广,并且能够防止条件覆盖缺失。编译器的这种自动检查可以避免一些常见的缺陷。例如,如果您向枚举类型添加了一个新值,编译器可以告诉您所有尚未覆盖该新情况的代码位置。

容错空间更小,胡闹的可能性也更低

if-else 链的一个问题是它允许对任何变量进行任意比较。对形式没有任何限制增加了隐藏错误的可能性。



if( expr == value_0 ) {
    ...
} else if( expr == value_1 ) {
    ...
} else if( expr2 == value_2 ) {
    ...
} else if( value_3 == expr ) {
    ...
}


Enter fullscreen mode Exit fullscreen mode

为什么里面会有 ` expr2in`?现在还不清楚这段代码是意在涵盖所有值,expr还是条件只是碰巧相似。

如果顺序颠倒呢value_3 == expr?这应该在代码审查中得到纠正,但这也可能造成混淆。

重构可能会导致这类问题。例如,一位程序员可能会修改表达式,修复错误或进行代码清理。在另一个并行分支中,另一位程序员添加了一个新的表达式。合并代码时,两种不同的代码形式会融合在一起,导致代码不一致。

减少重复

冗长的 if-else 语句链会造成不必要的语法开销。冗余是源代码的一大弊病。从前面的例子expr2可以看出,表达式的重复输入不容忽视。你不能仅仅因为看到一个 if-else 语句链就想当然地认为它像 switch 语句一样工作,因为它可能并非如此。冗余会增加阅读代码时的认知负担。

重复操作可能会影响性能,也可能导致其他错误。expr目前我只用过这种写法,但如果这个表达式是一个函数调用呢?



if( next_obj().get_status() == state_ready ) {
    ...
} else if( next_obj().get_status() == state_pending ) {
    ...
} else {
    ...
}


Enter fullscreen mode Exit fullscreen mode

这里第一个潜在的错误是调用了 `if` 函数next_obj。如果第一个条件为真,它只会执行一次。如果第一个条件为假,下一个 if 语句会再次调用该函数。它每次返回的值都相同吗?还是会遍历一个列表并递增?

那么,get_status()这个函数调用成本高还是低呢?它可能需要长时间计算或调用数据库?调用两次会使成本翻倍,这在很多情况下可能相当可观。

为了避免这两个问题,将这些值存储在临时变量中非常重要。遗憾的是,很多程序员都会忘记这一点,他们只是简单地复制粘贴第一个 if-else 语句,然后重复这个过程。



state = next_obj().get_status()
if( state == state_ready ) {
    ...
} else if( state == state_pending ) {
    ...
} else {
    ...
}


Enter fullscreen mode Exit fullscreen mode

switch你可以使用只计算一次表达式的语句来避免这个问题。

性能更佳

在许多情况下,switch 语句的性能优于 if-else 链。其严格的结构使得优化器能够轻松减少比较次数。

这是通过创建一个包含所有可能选项的二叉树来实现的。如果你的 switch 语句包含八个 case,那么只需要比较三次就能找到正确的 case。



switch( c ) {
    case 0: ...
    case 1: ...
    case 2: ...
    case 3: ...
    case 4: ...
    case 5: ...
    case 6: ...
    case 7: ...
}


Enter fullscreen mode Exit fullscreen mode

优化编译器或智能运行时可以将此问题简化为对数字进行二分查找。



if( c <4 ) {
    if( c < 2 ) {
        if( c == 0 ) {
            //0
        } else {
            //1
        }
    } else {
        if( c == 3 ) {
            // 3
        } else {
            // 4
        }
    }
} else {
    //repeated for 4...7
}


Enter fullscreen mode Exit fullscreen mode

一个聪明的优化器或许能以同样的方式识别 if-else 语句序列。但语句中可能存在的细微变化会降低这种可能性。例如,函数调用、隐藏赋值或使用备用变量都会阻止这种优化。

通过使用语义上有意义的高级形式,您可以为优化器提供更多改进代码的选项。

语言问题

然而,switch 语句并非完美无缺。特别是 C 和 C++ 中需要显式break声明的 switch 语句,存在一些问题。不过,它也允许将多个 case 组合在一起。

我很喜欢Python,但遗憾的是它没有switch语句。虽然映射和函数分派可以处理很多情况,但并不能处理所有情况。

Rust 的 switch 语句要好得多match。它保留了 switch 语句的高级语义,同时增加了更强大的模式匹配功能。虽然我并不喜欢 Rust 这门语言,但我认为它拥有最好的 switch 语句版本。我应该称之为模式匹配,这在语言设计中是这类特性的更通用名称。你也会在 Haskell 等其他语言中看到类似的功能。

也许这就是 switch 语句最大的问题所在switch。它感觉像是模式匹配的简化版。但这并不是完全放弃它、重新使用 if-else 语句的理由。switch 语句能够生成更简洁的代码,因为它们语义清晰、避免代码重复、并降低了出错的概率。

文章来源:https://dev.to/mortoray/why-switch-is-better-than-if-else-9cj