我应该使用switch语句还是if ... else链较长?


36

通常,当我听说switch语句时,将它推迟以替换长if ... else链。但是似乎当我使用switch语句时,我正在写更多的代码,而如果...否则我会写。您还遇到其他问题,例如将所有调用的所有变量都保持在同一范围内

这是一些代表我通常编写的流程的代码(感谢diam

String comment;   // The generated insult.
int which = (int)(Math.random() * 3);  //  Result is 0, 1, or 2.

if (which == 0) {
    comment = "You look so much better than usual.";
} else if (which == 1) {
    comment = "Your work is up to its usual standards.";
} else if (which == 2) {
    comment = "You're quite competent for so little experience.";
} else {
    comment = "Oops -- something is wrong with this code.";
}

然后他们要我用这个替换它:

String comment;   // The generated insult.
int which = (int)(Math.random() * 3);  //  Result is 0, 1, or 2.

switch (which) {
    case 0:  
             comment = "You look so much better than usual.";
    break;
    case 1:  
             comment = "Your work is up to its usual standards.";
    break;
    case 2:  
             comment = "You're quite competent for so little experience.";
    break;
    default: 
             comment = "Oops -- something is wrong with this code.";
}

似乎使用笨拙的语法编写了更多代码。但是使用switch语句真的有好处吗?


啊。是的,这肯定比较笨重,但仅在C系列中有效,因为它对case语句的语法非常丑陋。
梅森惠勒


1
您应尽可能避免两者。即使查找目标是函数或类,创建数据结构并执行查找也要好得多。
凯文·克莱恩

至少在.net中,切换速度更快。我不了解Java。
Knerd 2014年

可以重构为案例和方法的词典以及一个“如果”
Pavel Yermalovich

Answers:


57

对于这种特殊情况,在我看来,ifcase都是不好的选择。我将使用一个简单的数组:

String comments[] = {
    "You look so much better than usual.",
    "Your work is up to its usual standards.",
    "You're quite competent for so little experience."
};

String comment = comments[(int)(Math.random() * 3)];

附带说明,通常应该基于数组的大小来计算乘数,而不是对进行硬编码3

至于当你使用的情况下/开关,差异从级联if语句(或至少一个主要的区别)是switch可以半自动地优化基于数量和值的密度,而级联if语句叶编译器别无选择,只能在编写代码时生成代码,一个接一个地测试一个值,直到找到匹配项。仅涉及三个实际案例,这几乎不成问题,但有了足够的数量,它可能/可能会很重要。


这个例子只是顺便说一句。虽然不知道编译器可以像这样进行优化
TheLQ

6
@JBR威尔金森 在这种情况下,越界值只能通过编译器错误来实现,我不愿意花很多时间(我代码中的一个错误来测试结果的可能性与生成代码中的可能性差不多)。在一个超出范围的值是一个真正的问题的情况下(例如,正在从其他代码接收索引),我只会先检查边界,然后在检查之后才将其用作索引。
杰里·科芬

4
我认为这个答案是关于示例的非常具体的问题,而问题却比那个更笼统……
Khelben 2010年

3
@Khelben:在我看来,您没有花时间阅读整个答案。最后一段讨论了更广泛的问题。但是,有一个问题:我发现很少有情况适合我使用一条case语句或一系列if语句。大多数情况下,它们是某种(中等)替代地图/数组类型的事物的,最好还是直接使用后者。
杰里·科芬

1
@Titou:除非编译器完全死了,否则将在编译时构建一次数组,然后再使用静态结构。例如,如果您使用C或C ++进行此操作,则需要创建一个static const数组,以确保它始终存在(但问题中未提供任何语言,因此我也尝试不使用答案中的任何一种) )。
杰里·科芬

23

if...else if...链的问题在于,当我阅读它时,我必须查看每个单独的if条件以了解程序在做什么。例如,您可能会有这样的事情:

if (a == 1) {
    // stuff
} else if (a == 2) {
    // stuff
} else if (a == 3) {
    // stuff
} else if (b == 1) {
    // stuff
} else if (b == 2) {
    // stuff
}

(显然,对于少数这样的语句,还不错)

如果没有阅读每条语句,我将无法知道您在中途更改了条件变量。但是,由于switch仅将您限制为单个条件变量,因此我可以一目了然地看到发生了什么。

在一天结束的时候,不过,我也不喜欢switch或链if...else if。通常,更好的解决方案是针对某些情况(例如原始问题)或多态性(如果您的语言支持),使用某种跳转表或字典。当然,这并不总是可能的,但是我会寻找一个避免switch作为第一步的解决方案...


4
多态性的缺点是使代码片段化,与放在单个位置相比,很难理解。因此,在更改之前,您可能会有所犹豫。

为什么跳转表/字典比开关更好?
Titou

14
switch (which) {
  case 0: comment = "String 1"; break;
  case 1: comment = "String 2"; break;
  case 2: comment = "String 3"; break;
  default: comment = "Oops"; break;
}

上面写这种类型的开关盒的方式很普遍。之所以觉得开关箱更大,是因为您的身体只有一条线,而对于开关箱,您还需要break语句。因此,开关盒的外壳尺寸是其他外壳的两倍。使用更大量的代码,break语句不会对主体增加太多。对于单行主体,通常的做法是在case语句的同一行中编写代码。

正如其他人已经提到的那样,切换情况使意图更加清晰,您希望基于单个变量/表达式的值做出决定。我的评论纯粹是出于可读性的考虑,而不是基于性能。


1
如果将开关放在一个方法中并为每种情况return使用适当的字符串,则可以消除这些break语句。
罗伯特·哈维

您的解决方案也是最快的。
Titou

8

在这种情况下,switch语句更清楚地符合代码的意图:根据单个值选择要执行的操作。

另一方面,if语句更难阅读-您必须查看所有语句,以确保发生了什么。对我来说,它的代码更少(即使字符数可能会稍高一些),因为需要解析的代码也更少。


8

我同意Jerry的观点,在这种情况下,最好使用字符串数组,但总的来说,使用switch / case语句比使用elseifs链更好。它更容易阅读,并且有时编译器可以更好地优化这种方式,但是还有另一个好处:调试起来非常简单。

当您按下该开关时,您只需要一步来结束正确的分支,而不必一次小心地跨过多个if语句,并且可能击键太快并越过它并丢失了一些东西并有重新开始。


3

我更喜欢在这种情况下进行切换,它与代码点更好地匹配,对每个不同的输入值执行不同的语句。这些if..else行为更像是一种“技巧”来达到同样的效果。

switch 语句也更简洁,所有这些中都容易隐藏错字 ==

另外,对于C中的大块,切换速度更快。

else..if当您具有范围(在1至100之间,在100至200之间)时,或者在C中尝试使用诸如字符串的元素进行切换(在其他语言中可能)时,可以更合适。是一样的

当我用C编程时,我倾向于使用很多开关。


2

选择高效,简洁的内容,然后不仅记录您所做的事情,还记录原因。

可以重新访问代码,而并非总是由原始作者来访问。

有时您可能会故意选择一种实现而不是另一种实现,因为您正在对不存在的代码进行前瞻性思考。


2

我通常不喜欢这两种方法。长切换或if语句只是乞求重构为面向对象的抽象(但是您的示例我将其分类为短而不是长)。

我会亲自将这种代码包装到单独的帮助程序方法中。

private string GetInsult()
{
    int which = (int)(Math.random() * 3);  //  Result is 0, 1, or 2.

    switch (which) {
        case 0: return "You look so much better than usual.";
        case 1: return "Your work is up to its usual standards.";
        case 2: return "You're quite competent for so little experience.";
        default: return "Oops -- something is wrong with this code.";
    }
}

public void Foo()
{
    string comment = GetInsult();
    Print(comment);
}

将switch放置在单独的方法中,可以将return语句直接放置在switch语句内部(至少在c#中),而无需使用break语句,从而使代码更易于阅读。

这比if / else if / else if方法好得多。


3
我个人讨厌解决问题的“因为它看起来丑陋而采用另一种方法”。方法列表杂乱无章,恕我直言。我只会在以下情况下这样做:A)代码在某处重复或B)在其他地方可能有用
TheLQ 2012年

1
什么方法清单?为什么您的方法列表混乱不如代码混乱?我认为我们已经超越了“以一种单一的方式保存所有内容,以便您可以一次看到所有内容”的时代
sara 2016年

@TheLQ我总体上同意您的意见,但是在这种情况下,“ comment =“确实是由Pete的建议所考虑的。
Titou

0

在python中,没有switch语句,因为if / elif / else很不错:

a = 5

if a==1:
    print "do this"
elif a == 2:
    print "do that"
elif a == 3:
    print "do the other"
elif 3 < a < 9:
    print "do more"
elif 9 <= a < 15:
    print "do nothing"
else:
    print "say sorry"

简单吧?


Elif只是一个if语句,缺少一些字母。绝对更像是一条if语句,而不是switch语句。Python没有开关的事实使讨厌它们的人(像我一样)认为他们并不孤单。
Dan Rosenstark'1

Python格式化可在stackoverflow上使用,但不能在developers.stackexchange.com上使用:(
Christopher Mahan

meta除非是已知主题,否则您应该提醒他们。谢谢你给我一个见证人。
Dan Rosenstark 2012年


1
@Yar,让我想起了我的Wikipedia管理日……噢,乔伊。(我真的完全没话题了吗?)
Christopher Mahan 2012年

0

使C / C#样式switch特别令人讨厌的一件事是坚持case值是文字。关于VB / VB.NET的一件好事是,select/case每种情况都可以是任何布尔表达式。 很方便。只要一系列互斥的布尔表达式通常是有用的,一系列if / else ifs更灵活,更不用说键入和读取更高效了。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.