Is there any significant difference between using if/else and switch-case in C#?
使用
产生的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语句编译为一个条件分支加上一个间接跳转,而一个
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或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控制语句。
更新:
实际上,最好将字符串转换为大写(例如,
一点旁注:
要提高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之前确实学会了它们,它们是很接近的语言。