在 C#中使用switch
语句与使用语句的好处/缺点是什么。if/else
除了代码的外观之外,我无法想象会有那么大的不同。
产生的 IL 或相关的运行时性能有什么根本不同的原因吗?
在 C#中使用switch
语句与使用语句的好处/缺点是什么。if/else
除了代码的外观之外,我无法想象会有那么大的不同。
产生的 IL 或相关的运行时性能有什么根本不同的原因吗?
SWITCH 语句仅在调试或兼容模式下生成与 IF 相同的程序集。在发布时,它将被编译成跳转表(通过 MSIL 'switch' 语句)- 这是 O(1)。
C#(与许多其他语言不同)也允许打开字符串常量——这有点不同。为任意长度的字符串构建跳转表显然是不切实际的,因此大多数情况下这种开关将被编译成 IF 堆栈。
但是如果条件的数量足以覆盖开销,C# 编译器将创建一个 HashTable 对象,用字符串常量填充它并在该表上进行查找,然后跳转。哈希表查找不是严格的 O(1) 并且具有明显的常数成本,但是如果案例标签的数量很大,它将比比较 IF 中的每个字符串常数要快得多。
综上所述,如果条件数超过5个左右,则优先选择SWITCH而不是IF,否则使用看起来更好的任何东西。
通常(考虑到所有语言和所有编译器)switch 语句有时会比 if / else 语句更有效,因为编译器很容易从 switch 语句生成跳转表。给定适当的约束条件,可以对 if / else 语句做同样的事情,但这要困难得多。
在 C# 的情况下,这也是正确的,但出于其他原因。
对于大量字符串,使用 switch 语句有显着的性能优势,因为编译器将使用哈希表来实现跳转。
用少量的琴弦,两者的性能是一样的。
这是因为在这种情况下,C# 编译器不会生成跳转表。相反,它会生成相当于 IF / ELSE 块的 MSIL。
有一个“switch statement”MSIL指令,当jitted时会使用跳转表来实现switch语句。但是,它仅适用于整数类型(这个问题询问字符串)。
对于少量字符串,编译器生成 IF / ELSE 块比使用哈希表更有效。
当我最初注意到这一点时,我假设因为 IF / ELSE 块与少量字符串一起使用,所以编译器对大量字符串进行了相同的转换。
这是错误的。“IMA”很友善地向我指出了这一点(嗯……他对此并不友善,但他是对的,我错了,这是重要的部分)
我还对 MSIL 中缺少“switch”指令做出了一个愚蠢的假设(我想,如果有 switch 原语,他们为什么不将它与哈希表一起使用,所以一定没有 switch 原语。 ...)。这既是错误的,对我来说也是非常愚蠢的。'IMA' 再次向我指出了这一点。
我在这里进行了更新,因为它是评分最高的帖子,也是公认的答案。
但是,我将其设为 Community Wiki,因为我认为我不应该因为错误而获得 REP。如果您有机会,请为“ima”的帖子投票。
编译器会将几乎所有内容优化为相同的代码,但差异很小(Knuth,有人吗?)。
不同之处在于 switch 语句比 15 个 if else 语句串在一起更清晰。
朋友不要让朋友堆叠 if-else 语句。
选择的三个理由switch
:
针对本机代码的编译器通常可以将 switch 语句编译为一个条件分支和一个间接跳转,而if
s 序列需要一系列条件分支。根据案例的密度,已经写了很多关于如何有效地编译案例陈述的学术论文。有些是从lcc 编译器页面链接的。(Lcc 拥有用于开关的更具创新性的编译器之一。)
switch 语句是相互排斥的选项中的一种选择, switch 语法使这个控制流对程序员来说比 if-then-else 语句的嵌套更透明。
在某些语言中,包括 ML 和 Haskell,编译器会检查您是否遗漏了任何情况。我将此功能视为 ML 和 Haskell 的主要优势之一。我不知道 C# 是否可以做到这一点。
轶事:在他获得终身成就奖的演讲中,我听到托尼·霍尔说,在他职业生涯中所做的所有事情中,他最引以为豪的有三件事:
case
语句)我无法想象没有switch
.
实际上,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,它仍然不会像简单地检查零一样快。
通常它看起来会更好 - 即更容易理解正在发生的事情。考虑到性能优势充其量是极少的,代码的视图是最重要的区别。
因此,如果 if/else 看起来更好,请使用它,否则使用 switch 语句。
附带话题,但我经常担心(并且更经常看到)if
/else
和switch
语句在太多情况下变得太大。这些通常会损害可维护性。
常见的罪魁祸首包括:
修理:
如果您只是使用 if 或 else 语句,则基本解决方案正在使用比较?操作员
(value == value1) ? (type1)do this : (type1)or do this;
您可以在 switch 中执行 or 例程
switch(typeCode)
{
case TypeCode:Int32:
case TypeCode.Int64:
//dosomething here
break;
default: return;
}
根据此链接,使用 switch 和 if 语句的迭代测试的IF 与 Switch比较就像 1,000,000,000 次迭代,Switch Statement 花费的时间 = 43.0s & If Statement = 48.0s
这实际上是每秒 20833333次迭代,所以,我们是否真的需要更多地关注,
PS:只是为了知道一小部分条件的性能差异。
这实际上并不能回答您的问题,但鉴于编译版本之间几乎没有区别,我会敦促您以最能描述您的意图的方式编写代码。编译器不仅有更好的机会按照您的预期进行操作,而且还可以让其他人更容易维护您的代码。
如果您的意图是基于一个变量/属性的值来分支您的程序,那么 switch 语句最能代表该意图。
如果您的意图是根据不同的变量/属性/条件来分支您的程序,那么 if/else if 链最能代表该意图。
我承认 cody 关于人们忘记了 break 命令的说法是正确的,但我几乎同样经常看到人们在执行复杂的 if 块时会弄错 {},所以应该在条件语句中的行不是。这是我总是在 if 语句中包含 { } 的原因之一,即使其中只有一行。不仅更容易阅读,而且如果我需要在条件中添加另一行,我不能忘记添加它。
兴趣问题。几周前在工作中出现了这个问题,我们通过编写示例片段并在 .NET Reflector 中查看它找到了答案(reflector 太棒了!!我喜欢它)。
这就是我们所发现的:除字符串之外的任何内容的有效 switch 语句都被编译为 IL 作为 switch 语句。然而,如果它是一个字符串,它在 IL 中被重写为 if/else if/else。因此,在我们的案例中,我们想知道 switch 语句如何比较字符串,例如区分大小写等,并且反射器很快给了我们一个答案。知道这一点很有用。
如果要对字符串进行区分大小写的比较,则可以使用 switch 语句,因为它比在 if/else 中执行 String.Compare 更快。(编辑:阅读What is faster, switch on string or elseif on type?对于一些实际的性能测试)但是,如果您想做不区分大小写的操作,那么最好使用 if/else,因为生成的代码并不漂亮。
switch (myString.ToLower())
{
// not a good solution
}
最好的经验法则是在有意义的情况下(认真地)使用 switch 语句,例如:
如果您需要操纵该值以输入 switch 语句(创建一个临时变量以进行切换),那么您可能应该使用 if/else 控制语句。
更新:
实际上最好将字符串转换为大写(例如ToUpper()
),因为与ToLower()
. 这是一个微优化,但是在一个紧密的循环中它可能是有用的。
一点旁注:
要提高 switch 语句的可读性,请尝试以下操作:
switch 语句肯定比 if else 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 比 If 条件更快,看看是否有类似的程序:
编写一个程序输入任意数字(1 - 99 之间)并检查它在哪个插槽 a)1 - 9 然后插槽 1 b)11 - 19 然后插槽 2 c)21-29 然后插槽 3 依此类推直到 89- 99
如果你必须做很多条件,但儿子 Switch Case 你必须输入
开关(无/10)
在案例 0 = 1-9 ,案例 1 = 11-19 等等
会很容易
还有更多这样的例子!
switch 语句基本上是相等的比较。键盘事件比 switch 语句具有很大的优势,因为它易于编写和读取代码,然后 if elseif 语句会,缺少 {bracket} 也会带来麻烦。
char abc;
switch(abc)
{
case a: break;
case b: break;
case c: break;
case d: break;
}
如果(theAmountOfApples 大于 5 && theAmountOfApples 小于 10)如果(theAmountOfApples 大于 10 || theAmountOfApples == 100)出售您的苹果,则 if elseif 语句适用于多于一种解决方案。我不写 c# 或 c++,但我在学习 java 之前确实学习过它,它们是接近的语言。
switch 语句的一个可能缺点是缺少多个条件。您可以为 if (else) 设置多个条件,但不能在 switch 中设置具有不同条件的多个 case 语句。
Switch 语句不适用于超出简单布尔方程/表达式范围的逻辑运算。对于那些布尔方程/表达式,它非常适合,但不适用于其他逻辑运算。
您对 If 语句中可用的逻辑有更多的自由,但如果 If 语句变得笨拙或处理不当,可读性可能会受到影响。
根据您所面临的情况,两者都有。
我的 2 美分就可以了。大多数时候,如果性能不是一个标准,那么它更多的是关于代码的可读性。如果 if/else 语句的数量太多,比使用 switch 语句更好。