在C#中使用if/else和switch case有什么显著区别吗?

在C#中使用if/else和switch case有什么显著区别吗?,c#,.net,switch-statement,C#,.net,Switch Statement,在C#中使用开关语句与if/else语句相比有什么好处/坏处。除了代码的外观,我无法想象会有这么大的不同 产生的IL或相关运行时性能会有根本不同的原因吗 相关:实际上,switch语句更有效。编译器将对其进行优化,使其成为一个查找表,而使用if/else语句则无法找到该表。缺点是switch语句不能与变量值一起使用。 你不能这样做: switch(variable) { case someVariable break; default: break; } 一定是这样 s

在C#中使用
开关
语句与
if/else
语句相比有什么好处/坏处。除了代码的外观,我无法想象会有这么大的不同

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


相关:实际上,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语句。

通常情况下,这看起来会更好——即更容易理解发生了什么。考虑到性能方面的好处充其量是极小的,代码的视图是最重要的区别


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

我的cs教授建议不要切换语句,因为人们经常忘记中断或错误地使用它。我可以;I don’我想不起他到底说了些什么,但大致说来,看看一些开创性的代码库,其中显示了switch语句的示例(几年前),其中也有大量错误。

一般来说(考虑到所有语言和所有编译器),switch语句有时比if/else语句更有效,因为编译器很容易从switch语句生成跳转表。在适当的约束条件下,可以对if/else语句执行相同的操作,但这要困难得多

在C#的例子中,这也是正确的,但还有其他原因

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

对于少量字符串,两者之间的性能是相同的

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

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

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

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

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

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

我在这里做了更新,因为这是评级最高的帖子,也是公认的答案


然而,我之所以成为了社区维基,是因为我觉得我不应该因为自己的错误而受到指责。如果你有机会,请投票给“ima”的帖子。

旁白,但我经常担心(而且更经常看到)
如果
/
其他
开关
语句太大,案例太多。这些通常会损害可维护性

常见的罪魁祸首包括:

  • 在多个if语句中执行太多操作
  • 比人类所能分析的更多的案例陈述
  • if评估中的条件太多,无法了解正在查找的内容
  • 要修复:

  • 提取到方法重构
  • 使用带有方法指针的字典而不是案例,或者使用IoC来增加可配置性。方法工厂也会有所帮助
  • 将条件提取到自己的方法中

  • 这实际上并不能回答您的问题,但考虑到编译版本之间的差异不大,我建议您以最能描述您意图的方式编写代码。编译器不仅有更好的机会实现您期望的功能,而且会使其他人更容易维护您的代码

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

    如果您的意图是基于不同的变量/属性/条件来分支您的程序,那么If/else-If链最能代表该意图

    我承认cody关于人们忘记break命令的说法是正确的,但是我几乎经常看到人们在做复杂的if块时,他们把{}搞错了,所以应该在条件语句中的行就没有了。这是我总是在if语句中包含{}的原因之一,即使t
    switch (myString.ToLower())
    {
      // not a good solution
    }
    
    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;
    }
    
    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;
    }
    
    (value == value1) ? (type1)do this : (type1)or do this;
    
    switch(typeCode)
    {
       case TypeCode:Int32:
       case TypeCode.Int64:
         //dosomething here
         break;
       default: return;
    }
    
    char abc;
    switch(abc)
    {
    case a: break;
    case b: break;
    case c: break;
    case d: break;
    }