switch
与if/else
C#中的语句相比,使用语句有什么好处/缺点?除了您的代码外观之外,我无法想象有那么大的区别。
有什么原因导致最终的IL或相关的运行时性能完全不同?
switch
与if/else
C#中的语句相比,使用语句有什么好处/缺点?除了您的代码外观之外,我无法想象有那么大的区别。
有什么原因导致最终的IL或相关的运行时性能完全不同?
Answers:
在调试或兼容模式下,SWITCH语句仅生成与IF相同的程序集。在发行版中,它将被编译为跳转表(通过MSIL'switch'语句),即O(1)。
C#(与许多其他语言不同)还允许打开字符串常量-这有点不同。为任意长度的字符串构建跳转表显然不切实际,因此大多数情况下,此类开关将被编译为IF堆栈。
但是,如果条件数量大到足以支付开销,C#编译器将创建一个HashTable对象,用字符串常量填充该对象,并在该表上进行查找,然后跳转。哈希表查找严格来说不是O(1),并且具有明显的常量成本,但是如果case标签的数量很大,它将比与IF中的每个字符串常量相比快得多。
综上所述,如果条件数大于5左右,则应优先选择SWITCH而不是IF,否则请使用看起来更好的东西。
通常(考虑到所有语言和所有编译器),switch语句可以比if / else语句更有效,因为编译器很容易从switch语句生成跳转表。给定适当的约束,对于if / else语句可以执行相同的操作,但这要困难得多。
对于C#,也是如此,但由于其他原因。
对于大量的字符串,使用switch语句具有显着的性能优势,因为编译器将使用哈希表来实现跳转。
使用少量的字符串,两者之间的性能是相同的。
这是因为在这种情况下,C#编译器不会生成跳转表。而是生成等效于IF / ELSE块的MSIL。
有一条“ switch语句” MSIL指令,在被捷足先登时将使用跳转表来实现switch语句。但是,它仅适用于整数类型(此问题询问字符串)。
对于少量的字符串,编译器生成IF / ELSE块比使用哈希表更为有效。
当我最初注意到这一点时,我就做出了假设,因为IF / ELSE块使用的字符串数量很少,所以编译器对大量的字符串进行了相同的转换。
这是错误的。“ IMA”很友善地向我指出了这一点(嗯……他对此并不友善,但他是对的,而我是错的,这很重要)
我还对MSIL中缺少“切换”指令做了一个粗心的假设(我认为,如果有一个切换原语,为什么他们不将其与哈希表一起使用,所以一定不能有一个切换原语。 ...)。这既是错误的,也是我的愚蠢之举。“ IMA”再次向我指出了这一点。
我在此进行了更新,因为它是评分最高的帖子,也是被接受的答案。
但是,我把它做成Community Wiki是因为我认为我不应该因为错误而提出REP。如果有机会,请投票给'ima'职位。
选择以下三个原因switch
:
面向本机代码的编译器通常可以将switch语句编译为一个条件分支加上一个间接跳转,而if
s 序列则需要一系列条件分支。根据案例的密度,已经撰写了许多关于如何有效地编译案例陈述的学习论文。有些链接来自lcc编译器页面。(Lcc具有更创新的开关编译器之一。)
switch语句是相互排斥的选择之间的选择,并且switch语法使该控制流对程序员而言比if-then-else语句嵌套更为透明。
在某些语言(包括ML和Haskell)中,编译器将检查是否遗漏了任何情况。我将此功能视为ML和Haskell的主要优势之一。我不知道C#是否可以做到这一点。
轶事:在他获得终生成就奖的演讲中,我听到托尼·霍尔说他在职业生涯中所做的所有事情中,他最引以为傲的有三个:
case
语句)我无法想象没有switch
。
编译器将在几乎没有差异的情况下将几乎所有内容优化到同一代码中(Knuth,有人吗?)。
区别在于,如果其他语句串在一起,则switch语句比15条更干净。
朋友不要让朋友堆叠if-else语句。
实际上,switch语句更有效。编译器会将其优化为一个查找表,使用if / else语句无法查找。不利的一面是switch语句不能与变量值一起使用。
您不能:
switch(variable)
{
case someVariable
break;
default:
break;
}
它一定要是
switch(variable)
{
case CONSTANT_VALUE;
break;
default:
break;
}
我没有看到其他人提出(显而易见的?)观点,即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
}
与
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,它也不会像检查零一样快。
switch
兼容的,则该switch
语句会更好(可读性更高,有时更快)。 如果你知道一个情况下更可能多的,你可以拉说出来,形成一个if
- else
- switch
结构和,如果它是可测量的速度更快,你离开,在(重复,如果需要的话。)IMO这仍然相当可读。如果switch
退化并变得太小,则正则表达式替换将完成将其转换为else if
-chain的大部分工作。
这实际上并不能回答您的问题,但是鉴于编译版本之间的差异很小,我敦促您以最能描述您意图的方式编写代码。编译器不仅可以更好地完成您期望的操作,还可以使其他人更轻松地维护您的代码。
如果您打算基于一个变量/属性的值来分支程序,则switch语句最能代表该意图。
如果您打算基于不同的变量/属性/条件来分支程序,那么if / else if链最能代表该意图。
对于人们忘记了break命令,我会认为cody是对的,但是几乎我经常看到人们在if块弄错{}的情况下执行复杂的操作,因此条件语句中的行不是。这是即使在其中一行中也总是在 {}语句中包含{} 的原因之一。不仅更易于阅读,而且如果需要在条件中添加另一行,我也不会忘记添加它。
利息问题。几个星期前在工作中出现了这个问题,我们通过编写示例片段并在.NET Reflector中进行查看找到了答案(reflector太棒了!我喜欢它)。
这就是我们发现的:除字符串以外的任何有效切换语句都将作为切换语句编译到IL中。但是,如果它是字符串,则在IL中将其重写为if / else if / else。因此,在我们的案例中,我们想知道switch语句如何比较字符串(例如,区分大小写等),并且反射器很快为我们提供了答案。这很有用。
如果要对字符串进行区分大小写的比较,则可以使用switch语句,因为它比执行if.else中的String.Compare更快。(对于某些实际的性能测试,(编辑:请参阅什么更快捷,在字符串上打开或在类型上打开elseif?)。)但是,如果您想进行不区分大小写的操作,则最好使用if / else,因为结果代码不太漂亮。
switch (myString.ToLower())
{
// not a good solution
}
最好的经验法则是使用switch语句(如果很有意义),例如:
如果您需要操纵该值以将其馈送到switch语句(创建要进行切换的临时变量),则可能应该使用if / else控制语句。
更新:
实际上,最好将字符串转换为大写(例如ToUpper()
),因为与相比,即时编译器显然可以做进一步的优化ToLower()
。这是一个微优化,但是在紧密的循环中可能很有用。
一点注意事项:
要提高switch语句的可读性,请尝试以下操作:
switch语句肯定比if if更快。BlackWasp提供了速度测试
http://www.blackwasp.co.uk/SpeedTestIfElseSwitch.aspx
- 看看这个
但是在很大程度上取决于您要考虑的可能性,但是我会尽可能尝试使用switch语句。
我认为,不仅是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和相等性测试。
我的CS教授建议不要切换语句,因为很多时候人们会忘记休息或错误地使用它。我记不清他说的是什么,但是根据一些具有开创性的代码库(显示年前的switch语句示例)来看,其中也有很多错误。
我刚刚注意到的是,您可以组合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;
}
switch语句基本上是对相等性的比较。键盘事件相对于switch语句具有很大的优势,因为它易于编写和读取代码,因此if elseif语句如果缺少{括弧},也可能会造成麻烦。
char abc;
switch(abc)
{
case a: break;
case b: break;
case c: break;
case d: break;
}
if elseif语句最适合于一个解决方案,如果(theAmountOfApples大于5并且&theAmountOfApples小于10)保存您的苹果,否则,如果(theAmountOfApples大于10 || theAmountOfApples == 100)出售您的苹果。我不写c#或c ++,但是我在学习Java之前就已经学过了,它们是紧密的语言。