253

在 C#中使用switch语句与使用语句的好处/缺点是什么。if/else除了代码的外观之外,我无法想象会有那么大的不同。

产生的 IL 或相关的运行时性能有什么根本不同的原因吗?

相关:什么更快,打开字符串或 elseif 类型?

4

20 回答 20

390

SWITCH 语句仅在调试或兼容模式下生成与 IF 相同的程序集。在发布时,它将被编译成跳转表(通过 MSIL 'switch' 语句)- 这是 O(1)。

C#(与许多其他语言不同)也允许打开字符串常量——这有点不同。为任意长度的字符串构建跳转表显然是不切实际的,因此大多数情况下这种开关将被编译成 IF 堆栈。

但是如果条件的数量足以覆盖开销,C# 编译器将创建一个 HashTable 对象,用字符串常量填充它并在该表上进行查找,然后跳转。哈希表查找不是严格的 O(1) 并且具有明显的常数成本,但是如果案例标签的数量很大,它将比比较 IF 中的每个字符串常数要快得多。

综上所述,如果条件数超过5个左右,则优先选择SWITCH而不是IF,否则使用看起来更好的任何东西。

于 2008-12-28T08:06:05.887 回答
59

通常(考虑到所有语言和所有编译器)switch 语句有时会比 if / else 语句更有效,因为编译器很容易从 switch 语句生成跳转表。给定适当的约束条件,可以对 if / else 语句做同样的事情,但这要困难得多。

在 C# 的情况下,这也是正确的,但出于其他原因。

对于大量字符串,使用 switch 语句有显着的性能优势,因为编译器将使用哈希表来实现跳转。

用少量的琴弦,两者的性能是一样的。

这是因为在这种情况下,C# 编译器不会生成跳转表。相反,它会生成相当于 IF / ELSE 块的 MSIL。

有一个“switch statement”MSIL指令,当jitted时会使用跳转表来实现switch语句。但是,它仅适用于整数类型(这个问题询问字符串)。

对于少量字符串,编译器生成 IF / ELSE 块比使用哈希表更有效。

当我最初注意到这一点时,我假设因为 IF / ELSE 块与少量字符串一起使用,所以编译器对大量字符串进行了相同的转换。

这是错误的。“IMA”很友善地向我指出了这一点(嗯……他对此并不友善,但他是对的,我错了,这是重要的部分)

我还对 MSIL 中缺少“switch”指令做出了一个愚蠢的假设(我想,如果有 switch 原语,他们为什么不将它与哈希表一起使用,所以一定没有 switch 原语。 ...)。这既是错误的,对我来说也是非常愚蠢的。'IMA' 再次向我指出了这一点。

我在这里进行了更新,因为它是评分最高的帖子,也是公认的答案。

但是,我将其设为 Community Wiki,因为我认为我不应该因为错误而获得 REP。如果您有机会,请为“ima”的帖子投票。

于 2008-12-28T01:06:51.880 回答
19

编译器会将几乎所有内容优化为相同的代码,但差异很小(Knuth,有人吗?)。

不同之处在于 switch 语句比 15 个 if else 语句串在一起更清晰。

朋友不要让朋友堆叠 if-else 语句。

于 2008-12-28T00:22:37.697 回答
19

选择的三个理由switch

  • 针对本机代码的编译器通常可以将 switch 语句编译为一个条件分支和一个间接跳转,而ifs 序列需要一系列条件分支。根据案例的密度,已经写了很多关于如何有效地编译案例陈述的学术论文。有些是从lcc 编译器页面链接的。(Lcc 拥有用于开关的更具创新性的编译器之一。)

  • switch 语句是相互排斥的选项中的一种选择, switch 语法使这个控制流对程序员来说比 if-then-else 语句的嵌套更透明。

  • 在某些语言中,包括 ML 和 Haskell,编译器会检查您是否遗漏了任何情况。我将此功能视为 ML 和 Haskell 的主要优势之一。我不知道 C# 是否可以做到这一点。

轶事:在他获得终身成就奖的演讲中,我听到托尼·霍尔说,在他职业生涯中所做的所有事情中,他最引以为豪的有三件事:

  • 发明快速排序
  • 发明 switch 语句(托尼称之为case语句)
  • 开始和结束他在工业界的职业生涯

无法想象没有switch.

于 2008-12-28T06:27:55.540 回答
17

实际上,switch 语句更有效。编译器会将其优化为使用 if/else 语句无法找到的查找表。不利的一面是 switch 语句不能与变量值一起使用。
你不能这样做:

switch(variable)
{
   case someVariable
   break;
   default:
   break;
}

它一定要是

switch(variable)
{
  case CONSTANT_VALUE;
  break;
  default:
  break;
}
于 2008-12-28T00:20:53.783 回答
16

我没有看到其他人提出(显而易见的?)观点,即 switch 语句的假定效率优势取决于各种情况的可能性大致相同。在其中一个(或几个)值更有可能的情况下,if-then-else 阶梯可以更快,方法是确保首先检查最常见的情况:

因此,例如:

if (x==0) then {
  // do one thing
} else if (x==1) {
  // do the other thing
} else if (x==2) {
  // do the third thing
}

对比

switch(x) {
  case 0: 
         // do one thing
         break;
  case 1: 
         // do the other thing
         break;
  case 2: 
         // do the third thing
         break;
}

如果 x 90% 的时间为零,则“if-else”代码的速度可能是基于开关的代码的两倍。即使编译器将“开关”变成某种聪明的表驱动 goto,它仍然不会像简单地检查零一样快。

于 2009-10-15T22:32:16.880 回答
8

通常它看起来会更好 - 即更容易理解正在发生的事情。考虑到性能优势充其量是极少的,代码的视图是最重要的区别。

因此,如果 if/else 看起来更好,请使用它,否则使用 switch 语句。

于 2008-12-28T00:22:54.870 回答
4

附带话题,但我经常担心(并且更经常看到)if/elseswitch语句在太多情况下变得太大。这些通常会损害可维护性。

常见的罪魁祸首包括:

  1. 在多个 if 语句中做太多事情
  2. 比人工分析的案例陈述更多
  3. if 评估中的条件太多,无法知道要查找的内容

修理:

  1. 提取到方法重构。
  2. 使用带有方法指针的 Dictionary 而不是 case,或者使用 IoC 来增加可配置性。方法工厂也很有帮助。
  3. 提取条件到自己的方法
于 2008-12-28T04:09:01.560 回答
4

如果您只是使用 if 或 else 语句,则基本解决方案正在使用比较?操作员

(value == value1) ? (type1)do this : (type1)or do this;

您可以在 switch 中执行 or 例程

switch(typeCode)
{
   case TypeCode:Int32:
   case TypeCode.Int64:
     //dosomething here
     break;
   default: return;
}
于 2010-07-31T17:12:38.057 回答
4

根据此链接,使用 switch 和 if 语句的迭代测试的IF 与 Switch比较就像 1,000,000,000 次迭代,Switch Statement 花费的时间 = 43.0s & If Statement = 48.0s

这实际上是每秒 20833333次迭代,所以,我们是否真的需要更多地关注,

PS:只是为了知道一小部分条件的性能差异。

于 2013-10-27T06:13:39.660 回答
2

这实际上并不能回答您的问题,但鉴于编译版本之间几乎没有区别,我会敦促您以最能描述您的意图的方式编写代码。编译器不仅有更好的机会按照您的预期进行操作,而且还可以让其他人更容易维护您的代码。

如果您的意图是基于一个变量/属性的值来分支您的程序,那么 switch 语句最能代表该意图。

如果您的意图是根据不同的变量/属性/条件来分支您的程序,那么 if/else if 链最能代表该意图。

我承认 cody 关于人们忘记了 break 命令的说法是正确的,但我几乎同样经常看到人们在执行复杂的 if 块时会弄错 {},所以应该在条件语句中的行不是。这是我总是在 if 语句中包含 { } 的原因之一,即使其中只有一行。不仅更容易阅读,而且如果我需要在条件中添加另一行,我不能忘记添加它。

于 2008-12-28T06:21:32.790 回答
2

兴趣问题。几周前在工作中出现了这个问题,我们通过编写示例片段并在 .NET Reflector 中查看它找到了答案(reflector 太棒了!!我喜欢它)。

这就是我们所发现的:除字符串之外的任何内容的有效 switch 语句都被编译为 IL 作为 switch 语句。然而,如果它是一个字符串,它在 IL 中被重写为 if/else if/else。因此,在我们的案例中,我们想知道 switch 语句如何比较字符串,例如区分大小写等,并且反射器很快给了我们一个答案。知道这一点很有用。

如果要对字符串进行区分大小写的比较,则可以使用 switch 语句,因为它比在 if/else 中执行 String.Compare 更快。(编辑:阅读What is faster, switch on string or elseif on type?对于一些实际的性能测试)但是,如果您想做不区分大小写的操作,那么最好使用 if/else,因为生成的代码并不漂亮。

switch (myString.ToLower())
{
  // not a good solution
}

最好的经验法则是在有意义的情况下(认真地)使用 switch 语句,例如:

  • 它提高了代码的可读性
  • 您正在比较一系列值(浮点数、整数)或枚举

如果您需要操纵该值以输入 switch 语句(创建一个临时变量以进行切换),那么您可能应该使用 if/else 控制语句。

更新:

实际上最好将字符串转换为大写(例如ToUpper()),因为与ToLower(). 这是一个微优化,但是在一个紧密的循环中它可能是有用的。


一点旁注:

要提高 switch 语句的可读性,请尝试以下操作:

  • 把最有可能的分支放在第一位,即最常访问的分支
  • 如果它们都可能发生,请按字母顺序列出它们,以便更容易找到它们。
  • 永远不要对最后剩下的条件使用默认的包罗万象,这是懒惰的,并且会在代码的生命周期中导致问题。
  • 使用默认的包罗万象来断言未知条件,即使它极不可能发生。这就是断言的好处。
于 2008-12-28T11:54:36.237 回答
2

switch 语句肯定比 if else if 更快。BlackWasp 提供了速度测试

http://www.blackwasp.co.uk/SpeedTestIfElseSwitch.aspx

- 看看这个

但很大程度上取决于您要考虑的可能性,但我会尽可能使用 switch 语句。

于 2009-10-15T21:30:20.063 回答
1

我认为不仅仅是 C#,而是所有基于 C 的语言:因为开关仅限于常量,所以可以使用“跳转表”生成非常有效的代码。C 案例确实是一个很好的旧 FORTRAN 计算 GOTO,但 C# 案例仍然是针对常量进行测试。

优化器无法生成相同的代码。考虑,例如,

if(a == 3){ //...
} else if (a == 5 || a == 7){ //...
} else {//...
}

因为这些是复合布尔值,所以生成的代码必须计算一个值,然后短路。现在考虑等价的

switch(a){
   case 3: // ...
    break;
   case 5:
   case 7: //...
    break;
   default: //...
}

这可以编译成

BTABL: *
B3:   addr of 3 code
B5:
B7:   addr of 5,7 code
      load 0,1 ino reg X based on value
      jump indirect through BTABL+x

因为您隐含地告诉编译器它不需要计算 OR 和相等测试。

于 2008-12-28T00:21:45.887 回答
0

我的 cs 教授建议你不要切换语句,因为人们经常忘记休息或错误地使用它。我不记得他到底说了什么,但在查看一些显示 switch 语句示例的开创性代码库(几年前)也有很多错误。

于 2008-12-28T00:45:37.710 回答
0

我刚刚注意到的是,您可以结合 if/else 和 switch 语句!在需要检查先决条件时非常有用。

if (string.IsNullOrEmpty(line))
{
    //skip empty lines
}
else switch (line.Substring(0,1))
{
    case "1":
        Console.WriteLine(line);
        break;
    case "9":
        Console.WriteLine(line);
        break;
    default:
        break;
}
于 2009-10-02T13:48:25.783 回答
0

我认为 Switch 比 If 条件更快,看看是否有类似的程序:

编写一个程序输入任意数字(1 - 99 之间)并检查它在哪个插槽 a)1 - 9 然后插槽 1 b)11 - 19 然后插槽 2 c)21-29 然后插槽 3 依此类推直到 89- 99

如果你必须做很多条件,但儿子 Switch Case 你必须输入

开关(无/10)

在案例 0 = 1-9 ,案例 1 = 11-19 等等

会很容易

还有更多这样的例子!

于 2014-08-29T15:52:42.860 回答
0

switch 语句基本上是相等的比较。键盘事件比 switch 语句具有很大的优势,因为它易于编写和读取代码,然后 if elseif 语句会,缺少 {bracket} 也会带来麻烦。

char abc;
switch(abc)
{
case a: break;
case b: break;
case c: break;
case d: break;
}

如果(theAmountOfApples 大于 5 && theAmountOfApples 小于 10)如果(theAmountOfApples 大于 10 || theAmountOfApples == 100)出售您的苹果,则 if elseif 语句适用于多于一种解决方案。我不写 c# 或 c++,但我在学习 java 之前确实学习过它,它们是接近的语言。

于 2015-04-25T01:49:54.417 回答
0

switch 语句的一个可能缺点是缺少多个条件。您可以为 if (else) 设置多个条件,但不能在 switch 中设置具有不同条件的多个 case 语句。

Switch 语句不适用于超出简单布尔方程/表达式范围的逻辑运算。对于那些布尔方程/表达式,它非常适合,但不适用于其他逻辑运算。

您对 If 语句中可用的逻辑有更多的自由,但如果 If 语句变得笨拙或处理不当,可读性可能会受到影响。

根据您所面临的情况,两者都有。

于 2017-06-19T09:43:43.993 回答
0

我的 2 美分就可以了。大多数时候,如果性能不是一个标准,那么它更多的是关于代码的可读性。如果 if/else 语句的数量太多,比使用 switch 语句更好。

于 2020-10-18T02:30:10.533 回答