在c#中使用switch语句和if/else语句的优缺点是什么?除了代码的外观,我无法想象有这么大的区别。

是否有任何原因导致最终的IL或相关的运行时性能会有根本的不同?

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


当前回答

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

这是我们的发现: 对于字符串以外的任何对象,有效的switch语句将作为switch语句编译到IL。然而,如果它是一个字符串,它在IL中被重写为IF /else IF /else。所以在我们的例子中,我们想知道switch语句如何比较字符串,例如区分大小写等,reflector很快给了我们一个答案。知道这一点很有用。

如果你想对字符串进行区分大小写的比较,那么你可以使用switch语句,因为它比执行String更快。用if/else进行比较。(编辑:阅读哪个更快,打开字符串还是打开类型?然而,如果你想做一个不区分大小写的,那么最好使用if/else,因为结果代码不漂亮。

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

最好的经验法则是使用switch语句,如果它是有意义的(认真的),例如:

它提高了代码的可读性 您正在比较一个值范围(float, int)或enum

如果您需要操作将值送入switch语句(创建一个用于切换的临时变量),那么您可能应该使用If /else控制语句。

更新:

实际上更好的方法是将字符串转换为大写(例如ToUpper()),因为与ToLower()相比,即时编译器可以做进一步的优化。这是一个微观优化,但在一个紧密的循环中,它可能是有用的。


补充一点:

为了提高switch语句的可读性,可以尝试以下方法:

把最有可能的分支放在前面,即访问最多的分支 如果它们都可能发生,按字母顺序列出,这样更容易找到它们。 永远不要对剩下的最后一个条件使用默认的catch-all,这是懒惰的,并且会在以后的代码生命周期中导致问题。 使用默认的catch-all来断言一个未知的条件,即使它几乎不可能发生。这就是断言的好处。

其他回答

我没有看到其他人提出(明显的?)观点,即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
}

vs

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”代码的速度可以是基于开关的代码的两倍。即使编译器将“switch”转换为某种聪明的表驱动的goto,它仍然不会像简单地检查零那样快。

switch语句肯定比if else if语句快。有速度测试,已经提供了对它的黑黄蜂

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

——看看吧

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

实际上,switch语句更有效。编译器会将其优化为一个查找表,而使用if/else语句则不行。缺点是switch语句不能与变量值一起使用。

你不能:

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

它必须是:

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

我认为,不只是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和相等测试。

编译器将把几乎所有内容都优化为相同的代码,只有微小的差异(Knuth,任何人?)

区别在于一个switch语句比串在一起的15个if else语句更简洁。

朋友不会让朋友叠加if-else语句。