关于.NET:在C中使用if/else和switch case有什么显著的区别吗?

Is there any significant difference between using if/else and switch-case in C#?

使用switch声明与使用c中的if/else相比,有什么好处/缺点。除了代码的外观之外,我无法想象还有这么大的区别。

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

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


switch语句仅在调试或兼容模式下生成与ifs相同的程序集。在版本中,它将被编译成跳转表(通过msil的"switch"语句),即O(1)。

C(与许多其他语言不同)也允许打开字符串常量-这有点不同。显然,为任意长度的字符串构建跳转表是不实际的,因此通常这种切换会编译成一堆ifs。

但是,如果条件的数量足够大,足以覆盖开销,C编译器将创建一个哈希表对象,用字符串常量填充它,并在该表上进行查找,然后执行跳转。哈希表查找并不是严格的O(1),它有显著的固定成本,但是如果case标签的数量很大,那么它将比ifs中的每个字符串常量都快得多。

综上所述,如果条件的数量超过5个左右,最好是切换到if,否则使用任何看起来更好的。


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

对于C,这也是正确的,但出于其他原因。

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

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

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

有一条"switch statement"msil指令,在jitted时将使用跳转表来实现switch语句。但是,它只适用于整数类型(这个问题询问字符串)。

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

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

这是错误的。"伊玛很好地向我指出了这一点(嗯……他不喜欢,但他是对的,我错了,这是很重要的一部分)

我还对msil中缺少"switch"指令做了一个硬性假设(我认为,如果存在switch原语,为什么不将它与哈希表一起使用,那么就不能有switch原语……)这两个都是错的,而且对我来说是非常愚蠢的。"伊玛"再次向我指出了这一点。

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

但是,我已经创建了社区维基,因为我觉得我不应该因为这个代表的错误而受到指责。如果你有机会,请投票给伊玛。


选择switch的三个原因:

  • 针对本机代码的编译器通常可以将switch语句编译为一个条件分支加上一个间接跳转,而一个if序列则需要一个条件分支序列。根据案例的密度,已经撰写了大量关于如何有效地编译案例语句的学术论文;其中一些论文链接到LCC编译器页面。(LCC有一个更具创新性的交换机编译器。)

  • switch语句是互斥选项中的一种选择,switch语法使该控制流对程序员更透明,而不是if-then-else语句的嵌套。

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

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

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

我无法想象没有埃多克斯1〔2〕的生活。


实际上,switch语句更有效。编译器将把它优化到一个查找表,如果使用if/else语句,它将无法进行优化。缺点是switch语句不能与变量值一起使用。你不能这样做:

1
2
3
4
5
6
7
switch(variable)
{
   case someVariable
   break;
   default:
   break;
}

它必须是

1
2
3
4
5
6
7
switch(variable)
{
  case CONSTANT_VALUE;
  break;
  default:
  break;
}


编译器将把几乎所有的东西都优化到同一个代码中,并且有细微的差别(Knuth,任何人?).

区别在于switch语句比15 if else语句串在一起更干净。

朋友们不允许朋友们叠加if else语句。


我没看到有人提出(明显的?)指出switch语句的假定效率优势取决于各种情况的可能性大致相同。如果一个(或几个)值的可能性更大,那么if-then-else阶梯可以更快,方法是首先检查最常见的情况:

例如:

1
2
3
4
5
6
7
if (x==0) then {
  // do one thing
} else if (x==1) {
  // do the other thing
} else if (x==2) {
  // do the third thing
}

VS

1
2
3
4
5
6
7
8
9
10
11
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,它仍然不会像简单地检查零那么快。


通常情况下看起来会更好.考虑到性能的好处最多是非常小的,代码的视图是最重要的区别。

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


旁白,但我经常担心(而且更经常看到)if/elseswitch的声明太大,情况太多。这些通常会损害可维护性。

常见的罪魁祸首包括:

  • 在多个if语句内部执行过多操作
  • 比人类分析更多的案例陈述
  • 国际单项体育联合会评估中的条件太多,无法知道正在寻找什么
  • 修复:

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

  • 如果只使用if或else语句,则基本解决方案将使用比较?操作人员

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    switch(typeCode)
    {
       case TypeCode:Int32:
       case TypeCode.Int64:
         //dosomething here
         break;
       default: return;
    }


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

    如果您的意图是基于一个变量/属性的值对程序进行分支,那么switch语句最好地表示这个意图。

    如果您的意图是基于不同的变量/属性/条件对程序进行分支,那么一个if/else if链最能代表这个意图。

    我承认Cody对于忘记break命令的人是正确的,但几乎和我经常看到的一样,如果有人在遇到错误的块时执行复杂的操作,那么条件语句中应该出现的行就不是了。这是我总是在if语句中包含的原因之一,即使其中有一行。不仅更容易阅读,而且如果我需要在条件中添加另一行,我不能忘记添加它。


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

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

    --检查一下

    但这在很大程度上取决于您试图解释的可能性,但我尽量尽可能使用switch语句。


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

    这就是我们发现的:有效的switch语句将作为switch语句编译到il。但是,如果它是一个字符串,则会在IL中将其重写为if/else if/else。所以在我们的例子中,我们想知道switch语句如何比较字符串,例如区分大小写等,而reflector很快给了我们一个答案。这很有用。

    如果要对字符串进行区分大小写的比较,则可以使用switch语句,因为它比执行字符串更快。请在if/else中进行比较。(编辑:读什么更快,打开字符串或Elseif打开类型?但是,对于一些实际的性能测试),如果您希望不区分大小写,那么最好使用if/else,因为生成的代码并不漂亮。

    1
    2
    3
    4
    switch (myString.ToLower())
    {
      // not a good solution
    }

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

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

    如果需要操作要馈送到switch语句中的值(创建一个临时变量进行切换),那么您可能应该使用if/else控制语句。

    更新:

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

    一点旁注:

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

    • 将最可能的分支放在第一位,即访问最多的分支
    • 如果它们都可能发生,请按字母顺序列出,这样更容易找到它们。
    • 不要在最后一个剩余的条件中使用默认的catch all,这是很懒惰的,并且会在代码生命周期的后期引起问题。
    • 使用默认的catch all断言一个未知的条件,即使它极不可能发生。这就是断言的好处。


    根据此链接,使用switch和if语句的迭代测试的if-vs-switch比较,与100000000次迭代类似,switch语句花费的时间=43.0s&by-if语句=48.0s

    实际上是每秒20833333次迭代,所以,如果我们真的需要集中更多的精力,

    P.S:只是想知道一小部分条件下的性能差异。


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

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

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    switch(a){
       case 3: // ...
        break;
       case 5:
       case 7: //...
        break;
       default: //...
    }

    这可以编译成

    1
    2
    3
    4
    5
    6
    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

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


    我认为切换比IF条件更快比如看是否有这样的程序:

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

    然后,如果你必须做许多条件,但子开关箱,你必须键入

    Switch ( no /10 )

    and on case 0 = 1-9 ,case 1 = 11-19 and so on

    会很容易的

    还有很多这样的例子!


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

    switch语句不适用于简单布尔公式/表达式范围之外的逻辑操作。对于这些布尔方程/表达式,它非常适合,但不适用于其他逻辑运算。

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

    两者都有自己的位置,这取决于你所面对的环境。


    我刚刚注意到的一点是,您可以将if/else和switch语句结合起来!在需要检查前提条件时非常有用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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;
    }


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
        $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);

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
        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;
        }

    我的CS教授建议你不要转换语句,因为人们经常忘记中断或错误地使用它。我记不清他说了些什么,但从一行代码中可以看出,一些具有开创性的代码库显示了switch语句的例子(几年前),其中也有很多错误。


    switch语句基本上是对等式的比较。键盘事件比switch语句有很大的优势,当它具有易于编写和读取的代码时,if-elseif语句也会出现问题,缺少一个括号。

    1
    2
    3
    4
    5
    6
    7
    8
    char abc;
    switch(abc)
    {
    case a: break;
    case b: break;
    case c: break;
    case d: break;
    }

    如果一个if-elseif语句对于一个以上的解决方案是很好的,如果(mountofapples大于5&;themountofapples小于10),请保存您的苹果否则,如果(苹果的数量大于10苹果的数量=100),请出售您的苹果。我不写C语言或C++,但我在学习Java之前确实学会了它们,它们是很接近的语言。