为什么切换/大小写而不是如果/否则如果?
这个问题主要针对C / C ++,但我猜其他语言也是相关的。
我不明白为什么开关/大小写仍然被用来代替if / else if。 在我看来,就像使用goto's一样,结果是相同的混乱的代码,而同样的结果可以通过if / else if以更有组织的方式实现。
不过,我经常看到这些街区。 find它们的一个常见的地方是在一个消息循环(WndProc …)附近,而当它们引发最严重的破坏时,这些地方就是这样:variables在整个块中共享,即使不是主动的(也不可能是在里面初始化)。 必须特别注意不要突破,等等…
就个人而言,我避免使用它们,我想知道我错过了什么吗?
他们比if / else更有效率吗? 他们是否inheritance了传统?
总结我最初的文章和评论 – switch
语句优于if
/ else
语句有几个优点:
-
清洁代码。 代码与多个链接的
if
/else if ...
看起来凌乱,难以维护 –switch
结构更清洁。 -
性能。 对于高密度的
case
编译器生成跳转表,进行稀疏二分search或者一系列if
/else
,所以在最坏的情况下,switch
和if
/else
一样快,但通常更快。 尽pipe一些编译器可以类似地优化if
/else
。 -
testing顺序无关紧要。 为了加速一系列的
if
/else
testing,首先需要把更多可能的情况。 用switch
/case
程序员不需要考虑这个问题。 -
默认可以在任何地方。 有了
if
/else
默认情况必须在最后 – 在最后一个之后。 在switch
–default
可以在任何地方,程序员发现它更合适。 -
通用代码。 如果您需要为多个案例执行通用代码,您可能会忽略
break
并且执行会“落空” – 这是if
/else
无法实现的。 (对于这种情况,有一个很好的做法,就是发表一个特殊的评论/* FALLTHROUGH */
lint认可它,不会抱怨,如果没有这个评论,它会发出抱怨,因为这是常见错误而忘记了break
)。
感谢所有评论者。
那么,一个原因是明确的….
如果你有一个开关/情况下,那么expression式不能改变….即
switch (foo[bar][baz]) { case 'a': ... break; case 'b': ... break; }
而用if / else,如果你写错了(或意图):
if (foo[bar][baz] == 'a') { .... } else if (foo[bar][baz+1] == 'b') { .... }
阅读你的代码的人会想知道“fooexpression应该是相同的”还是“为什么它们是不同的”?
请记住case / select提供了额外的灵活性:
- 条件评估一次
- 足够灵活,可以构buildDuff的设备
- 贯彻落实(又名无中断)
以及它历史上执行得更快 (通过跳转/查找表)*
还要记住,switch语句允许继续控制stream,这允许您很好地组合条件,同时允许您为某些条件添加额外的代码,如下面的代码段所示:
switch (dayOfWeek) { case MONDAY: garfieldUnhappy = true; case TUESDAY: case WEDNESDAY: case THURSDAY: case FRIDAY: weekDay = true; break; case SATURDAY: weekendJustStarted = true; case SUNDAY: weekendDay = true; break; }
在这里使用if/else
语句不会在任何地方变得很好。
if (dayOfWeek == MONDAY) { garfieldUnhappy = true; } if (dayOfWeek == SATURDAY) { weekendJustStarted = true; } if (dayOfWeek == MONDAY || dayOfWeek == TUESDAY || dayOfWeek == WEDNESDAY || dayOfWeek == THURSDAY || dayOfWeek == FRIDAY) { weekDay = true; } else if (dayOfWeek == SATURDAY || dayOfWeek == SUNDAY) { weekendDay = true; }
如果有很多情况,switch语句看起来更清晰。
当你有多个你想要的相同行为的值时,也是很好的 – 只要使用多个落到单个实现中的“case”语句就比if(this || that || someotherthing || .. 。)
它也可能取决于你的语言 – 例如,一些语言切换只能使用数字types,所以当你使用枚举值,数字常量等等时,它可以节省一些input。
If (day == DAYOFWEEK_MONDAY) { //... } else if (day == DAYOFWEEK_TUESDAY) { //... } //etc...
或稍微容易阅读…
switch (day) { case DAYOFWEEK_MONDAY : //... case DAYOFWEEK_TUESDAY : //... //etc... }
switch / case通常比if / else if / else更高效,但偶尔(取决于语言和编译器)转换为简单的if / else if / else语句。
我个人认为switch语句使得代码比一堆if语句更可读; 只要你遵循一些简单的规则。 规则你甚至应该遵循你的if / else if / else情况,但这又是我的意见。
这些规则:
- 从来没有,你的交换机上有多条线路。 调用一个方法或函数,在那里做你的工作。
- 始终检查是否中断/案例贯穿。
- 冒泡例外。
明晰。 正如我在这里所说, else if
有问题的线索是
ELSE IF的使用频率比语法所允许的要严格得多。 这是一个灵活的大锤,允许完全无关的条件进行testing。 但是,它通常用于扑灭CASE的苍蝇,比较相同的expression式与其他值…
这降低了代码的可读性。 由于结构允许有条件的复杂性,读者在parsingELSE IF时需要考虑更多的可能性,而不是parsingCASE。
实际上,一个switch语句意味着你正在处理的是一个或多或less的枚举,这个枚举给你一个即时的线索。
也就是说,任何一个OO语言的枚举开关都可能编码得更好 – 但是对于相同的值,一系列的if / else可能意味着相同的糟糕devise概率。
解决了交换机内部的所有东西都具有相同范围的担忧,您可以随时将您的案例逻辑放到另一个{}块中,像这样..
switch( thing ) { case ONETHING: { int x; // local to the case! ... } break; case ANOTHERTHING: { int x; // a different x than the other one } break; }
..现在我不是说这很漂亮。 只要把它放在那里就是可能的事情,如果你绝对不得不把事情隔离开来。
关于范围问题的另一个想法 – 似乎是一个很好的做法,只把一个开关放在一个函数中,而不是其他的。 在这种情况下,variables作用域并不是那么重要,因为这样你通常只能处理任何给定的函数调用的情况。
好的,最后一个想法是:如果一个函数包含多个开关,那么可能是重构代码的时候了。 如果一个函数包含嵌套的开关,这可能是重新思考你的devise的一个线索=)
可以肯定的是,它们编译的结果与If / ElseIf相同,但是当超过2或3个“elses”时,我发现Switch / Case更容易阅读。
switch case主要用来在编程时做出select。这与条件语句无关:
如果你的程序只需要做出select,那你为什么要使用if / else块,并增加编程工作量,同时又会降低程序的执行速度。
开关语句可以针对速度进行优化,但是如果案例值分散在大量的值上,则会占用更多的内存。
if / else通常很慢,因为每个值都需要检查。
Smalltalker可能会拒绝switch和if-then-else,并可能会写下如下内容:
shortToLongDaysMap := Dictionary new. shortToLongDaysMap at: 'Mon' put: 'Monday'; at: 'Tue' put: 'Tuesday'; at: 'Wed' put: 'Wednesday' etc etc. longForm := shortToLongDaysMap at: shortForm ifAbsent: [shortForm]
这是一个微不足道的例子,但我希望你能看到这种技术如何在大量的情况下扩展。
请注意at:IfAbsent:
的第二个参数at:IfAbsent:
与case语句的default子句相似。
这背后的主要原因是可维护性和可读性。 它很容易使代码更易于阅读和维护开关/ case语句,然后if / else。 因为你有很多if / else,那么代码变得像nest一样非常混乱,而且很难维护它。
还有一些执行时间是另一个原因。