在C#中使用if / else和switch-case之间是否有任何显着差异?

时间:2008-12-28 00:16:48

标签: c# .net switch-statement

在C#中使用switch语句与if/else的好处/缺点是什么?我无法想象除了你的代码外观之外还有其他重大差异。

为什么生成的IL或相关的运行时性能会有根本的不同?

相关:What is quicker, switch on string or elseif on type?

21 个答案:

答案 0 :(得分:302)

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

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

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

总结一下,如果条件数超过5个,则选择SWITCH而不是IF,否则使用看起来更好的东西。

答案 1 :(得分:48)

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

对于C#,情况也是如此,但出于其他原因。

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

使用少量字符串,两者之间的性能是相同的。

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

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

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

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

这是错误的。 'IMA'非常友好地指出了这一点(嗯......他对此不好,但他是对的,我错了,这是重要的部分)

我还对MSIL中缺少“切换”指令做了一个假设(我想,如果有一个切换原语,他们为什么不用哈希表使用它,所以一定不能有切换原语....)。这既错了,又让我非常愚蠢。再次'IMA'向我指出了这一点。

我在这里做了更新,因为它是评分最高的帖子和接受的答案。

然而,我已经把它作为社区维基,因为我觉得我不配错了REP。如果你有机会,请投票'ima'的帖子。

答案 2 :(得分:17)

偏好switch的三个理由:

  • 针对本机代码的编译器通常可以将switch语句编译为一个条件分支加上间接跳转,而if s序列需要条件序列分支即可。根据案件的密度,已经写了很多关于如何有效地编写案例陈述的学术论文;有些是从lcc compiler page链接的。 (Lcc有一个更具创新性的交换机编译器。)

  • switch语句是互斥备选方案中的选择,并且切换语法使该控制流程对程序员更加透明然后是if-then的嵌套 - 陈述。

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

一则轶事:在他获得终身成就奖的演讲中,我听到Tony Hoare说他在职业生涯中所做的一切,有三件他最为自豪:

  • 发明Quicksort
  • 创建switch语句(Tony称之为case语句)
  • 开始和结束他的职业生涯

无法想象没有switch 的生活。

答案 3 :(得分:14)

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

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

必须是

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

答案 4 :(得分:14)

编译器会将几乎所有内容优化到相同的代码中,只有很小的差异(Knuth,任何人?)。

不同之处在于,如果将else语句串在一起,则switch语句比十五个更清晰。

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

答案 5 :(得分:12)

我没有看到其他人提出(显而易见的?)这一点,即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”代码的速度可以是基于交换机的代码的两倍。即使编译器将“开关”转换为某种聪明的表驱动goto,它仍然不会像检查零那样快。

答案 6 :(得分:7)

通常会看起来更好 - 也就是说会更容易理解发生了什么。考虑到性能优势最多只是极其微小,代码视图是最重要的区别。

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

答案 7 :(得分:4)

附带主题,但我经常担心(并且经常看到)if / elseswitch语句过于庞大且案例太多。这些通常会损害可维护性。

常见的罪魁祸首包括:

  1. 在多个if语句中做太多内容
  2. 比人类更可能分析的案例陈述
  3. if评估中有太多条件可以知道正在寻找什么
  4. 修复:

    1. 提取到方法重构。
    2. 使用带有方法指针而不是大小写的字典,或使用IoC来增加可配置性。方法工厂也很有帮助。
    3. 将条件提取到自己的方法

答案 8 :(得分:3)

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

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

您可以在开关中执行或例程

switch(typeCode)
{
   case TypeCode:Int32:
   case TypeCode.Int64:
     //dosomething here
     break;
   default: return;
}

答案 9 :(得分:2)

根据此链接,IF vs Switch使用switch和if语句进行迭代测试的比较,类似于1,000,000,000次迭代,切换语句= 43.0s &通过如果声明 = 48.0s

每秒迭代次数 20833333 ,那么,我们是否真的需要关注更多,

P.S:只知道小条件列表的性能差异。

答案 10 :(得分:2)

这实际上并没有回答你的问题,但考虑到编译版本之间没有什么区别,我建议你以最能描述你意图的方式编写代码。编译器不仅有更好的机会按照您的期望进行操作,而且还可以让其他人更轻松地维护您的代码。

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

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

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

答案 11 :(得分:2)

switch语句肯定比if if if更快。 BlackWasp提供了最快的速度

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

- 检查出来

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

答案 12 :(得分:1)

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

优化器不能生成相同的代码。考虑一下,例如,

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

因为那些是复合布尔值,所生成的代码必须计算一个值和shortcircuit。现在考虑等效的

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和相等测试。

答案 13 :(得分:1)

兴趣问题。这是几个星期前在工作中出现的,我们通过编写一个示例片段并在.NET Reflector中查看它来找到答案(反射器真棒!我喜欢它)。

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

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

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

最好的经验法则是使用switch语句(严重),例如:

  • 它提高了代码的可读性
  • 您正在比较一系列值(float,int)或枚举

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

更新:

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


一点注意事项:

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

  • 首先放置最可能的分支,即最多访问
  • 如果它们都可能发生,请按字母顺序列出,以便更容易找到它们。
  • 永远不要使用默认的catch-all来保留最后剩余的条件,这是懒惰的,并且会在代码的生命周期中引起问题。
  • 使用默认的catch-all来声明未知条件,即使它不太可能发生。这就是断言的好处。

答案 14 :(得分: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;
}

答案 15 :(得分:0)

我的cs教授建议不要切换语句,因为人们常常忘记中断或使用不正确。我无法回想起他所说的内容,但是我看到一些开创性的代码库,其中显示了switch语句的例子(多年前)也有很多错误。

答案 16 :(得分:0)

我认为开关比条件更快 比如看看是否有类似的程序:

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

然后如果你必须做出很多条件,那么你必须输入儿子切换案例

  

切换(否/ 10)

     

且案例0 = 1-9,案例1 = 11-19,依此类推

它会如此简单

还有更多这样的例子!

答案 17 :(得分:0)

switch语句基本上是对等的比较。键盘事件比switch语句有很大的优势,当易于编写和读取代码然后if ifif语句时,错过{bracket}也会让人感到麻烦。

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

如果(theAmountOfApples大于5&& theAmountOfApples小于10),if elseif语句对于多个解决方案非常有用保存你的苹果 否则if(theAmountOfApples大于10 || theAmountOfApples == 100)卖掉你的苹果。我不写c#或c ++,但在学习java之前我确实学过它,而且它们是接近的语言。

答案 18 :(得分:0)

我知道这不是问题,但我真的需要指出的是,当您考虑该级别的效率时,您可能需要在代码中进行更多抽象。您将不再需要切换案例,特别是如果它包含逻辑。 (我在php中的例子)。

    $feeMapping = [
        1000 => 1,
        2000 => 2,
        3000 => 3,
        4000 => 4,
        5000 => 5,
        6000 => 6,
        7000 => 7
    ];

    function findFee($feeMapping, $amount) {
        foreach ($feeMapping as $fee => $value) {
            if ($value >= $amount) {
                return $fee;
            }
        }

        return 7;
    }

    $feeValue = findFee($feeMapping, 200);

现在看看类似代码的冗余!

    if ($amount >= 1000) {
        return 1;
    } elseif ($amount >= 2000) {
        return 2;
    } elseif ($amount >= 3000) {
        return 3;
    } elseif ($amount >= 4000) {
        return 4;
    } elseif ($amount >= 5000) {
        return 5;
    } elseif ($amount >= 6000) {
        return 6;
    } else {
        return 7;
    }

答案 19 :(得分:0)

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

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

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

根据你所面对的情况,两者都有。

答案 20 :(得分:-1)

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