假设我可以选择在字符串比较的基础上识别代码路径,或者如果确定类型:
哪个更快,为什么?
switch(childNode.Name) { case "Bob": break; case "Jill": break; case "Marko": break; } if(childNode is Bob) { } elseif(childNode is Jill) { } else if(childNode is Marko) { }
更新:我问这个问题的主要原因是因为switch语句对于什么算是一个特例.例如,它不允许您使用变量,只允许移动到主程序集的常量.我认为它有这个限制,因为它正在做一些时髦的东西.如果它只是翻译成elseifs(作为一张海报评论)那么为什么我们不允许在case语句中使用变量?
警告:我是后期优化的.这种方法被称为许多在应用程序的缓慢一部分倍.
Greg的配置文件结果非常适合他所涉及的确切场景,但有趣的是,当考虑许多不同的因素(包括被比较的类型的数量)以及基础数据中的相对频率和任何模式时,不同方法的相对成本会发生显着变化. .
简单的答案是,没有人能够告诉您在特定情况下的性能差异,您需要在自己的系统中以不同的方式测量性能,以获得准确的答案.
If/Else链是少数类型比较的有效方法,或者如果您可以可靠地预测哪些类型将构成您看到的大部分类型.该方法的潜在问题是随着类型数量的增加,必须执行的比较次数也会增加.
如果我执行以下内容:
int value = 25124;
if(value == 0) ...
else if (value == 1) ...
else if (value == 2) ...
...
else if (value == 25124) ...
必须在输入正确的块之前评估每个先前的if条件.另一方面
switch(value) {
case 0:...break;
case 1:...break;
case 2:...break;
...
case 25124:...break;
}
将执行一次简单的跳转到正确的代码位.
在你的例子中它变得更复杂的是你的另一个方法使用字符串而不是整数的开关,这会变得更复杂一些.在较低级别,字符串无法以与整数值相同的方式打开,因此C#编译器可以为您提供一些魔力.
如果switch语句"足够小"(编译器执行它认为最好的自动),则切换字符串会生成与if/else链相同的代码.
switch(someString) {
case "Foo": DoFoo(); break;
case "Bar": DoBar(); break;
default: DoOther; break;
}
是相同的:
if(someString == "Foo") {
DoFoo();
} else if(someString == "Bar") {
DoBar();
} else {
DoOther();
}
一旦字典中的项列表变得"足够大",编译器将自动创建一个内部字典,该字典从交换机中的字符串映射到整数索引,然后基于该索引切换.
它看起来像这样(想象更多的条目比我打扰打字)
静态字段在"隐藏"位置中定义,该位置与包含类型的switch语句的类相关联,Dictionary
并给出错位名称
//Make sure the dictionary is loaded
if(theDictionary == null) {
//This is simplified for clarity, the actual implementation is more complex
// in order to ensure thread safety
theDictionary = new Dictionary();
theDictionary["Foo"] = 0;
theDictionary["Bar"] = 1;
}
int switchIndex;
if(theDictionary.TryGetValue(someString, out switchIndex)) {
switch(switchIndex) {
case 0: DoFoo(); break;
case 1: DoBar(); break;
}
} else {
DoOther();
}
在我刚刚运行的一些快速测试中,If/Else方法的速度是3种不同类型(其中类型随机分布)的开关的3倍.在25种类型中,开关速度较快(16%),50种类型,开关速度快两倍以上.
如果您要打开大量类型,我建议使用第三种方法:
private delegate void NodeHandler(ChildNode node);
static Dictionary TypeHandleSwitcher = CreateSwitcher();
private static Dictionary CreateSwitcher()
{
var ret = new Dictionary();
ret[typeof(Bob).TypeHandle] = HandleBob;
ret[typeof(Jill).TypeHandle] = HandleJill;
ret[typeof(Marko).TypeHandle] = HandleMarko;
return ret;
}
void HandleChildNode(ChildNode node)
{
NodeHandler handler;
if (TaskHandleSwitcher.TryGetValue(Type.GetRuntimeType(node), out handler))
{
handler(node);
}
else
{
//Unexpected type...
}
}
这类似于Ted Elliot所建议的,但运行时类型句柄而不是完整类型对象的使用避免了通过反射加载类型对象的开销.
以下是我机器上的一些快速计时:
Testing 3 iterations with 5,000,000 data elements (mode=Random) and 5 types Method Time % of optimal If/Else 179.67 100.00 TypeHandleDictionary 321.33 178.85 TypeDictionary 377.67 210.20 Switch 492.67 274.21 Testing 3 iterations with 5,000,000 data elements (mode=Random) and 10 types Method Time % of optimal If/Else 271.33 100.00 TypeHandleDictionary 312.00 114.99 TypeDictionary 374.33 137.96 Switch 490.33 180.71 Testing 3 iterations with 5,000,000 data elements (mode=Random) and 15 types Method Time % of optimal TypeHandleDictionary 312.00 100.00 If/Else 369.00 118.27 TypeDictionary 371.67 119.12 Switch 491.67 157.59 Testing 3 iterations with 5,000,000 data elements (mode=Random) and 20 types Method Time % of optimal TypeHandleDictionary 335.33 100.00 TypeDictionary 373.00 111.23 If/Else 462.67 137.97 Switch 490.33 146.22 Testing 3 iterations with 5,000,000 data elements (mode=Random) and 25 types Method Time % of optimal TypeHandleDictionary 319.33 100.00 TypeDictionary 371.00 116.18 Switch 483.00 151.25 If/Else 562.00 175.99 Testing 3 iterations with 5,000,000 data elements (mode=Random) and 50 types Method Time % of optimal TypeHandleDictionary 319.67 100.00 TypeDictionary 376.67 117.83 Switch 453.33 141.81 If/Else 1,032.67 323.04
至少在我的机器上,当用作方法输入的类型的分布是随机的时,类型句柄字典方法胜过15种不同类型的所有其他类型.
另一方面,如果输入完全由在if/else链中首先检查的类型组成,那么方法要快得多:
Testing 3 iterations with 5,000,000 data elements (mode=UniformFirst) and 50 types Method Time % of optimal If/Else 39.00 100.00 TypeHandleDictionary 317.33 813.68 TypeDictionary 396.00 1,015.38 Switch 403.00 1,033.33
相反,如果输入始终是if/else链中的最后一个,那么它具有相反的效果:
Testing 3 iterations with 5,000,000 data elements (mode=UniformLast) and 50 types Method Time % of optimal TypeHandleDictionary 317.67 100.00 Switch 354.33 111.54 TypeDictionary 377.67 118.89 If/Else 1,907.67 600.52
如果您可以对输入做出一些假设,那么您可以从混合方法中获得最佳性能,其中您执行if/else检查最常见的几种类型,然后如果这些方法失败则回退到字典驱动方法.
我刚刚实现了一个快速测试应用程序并使用ANTS 4进行了分析.
规范:32位Windows XP中的.Net 3.5 sp1,代码以发布模式构建.
300万次测试:
开关:1.842秒
如果:0.344秒.
此外,switch语句结果显示(不出所料)较长的名称需要更长时间.
100万次测试
鲍勃:0.612秒.
吉尔:0.835秒.
马可:1.093秒.
我看起来像"If Else"更快,至少我创建的场景.
class Program { static void Main( string[] args ) { Bob bob = new Bob(); Jill jill = new Jill(); Marko marko = new Marko(); for( int i = 0; i < 1000000; i++ ) { Test( bob ); Test( jill ); Test( marko ); } } public static void Test( ChildNode childNode ) { TestSwitch( childNode ); TestIfElse( childNode ); } private static void TestIfElse( ChildNode childNode ) { if( childNode is Bob ){} else if( childNode is Jill ){} else if( childNode is Marko ){} } private static void TestSwitch( ChildNode childNode ) { switch( childNode.Name ) { case "Bob": break; case "Jill": break; case "Marko": break; } } } class ChildNode { public string Name { get; set; } } class Bob : ChildNode { public Bob(){ this.Name = "Bob"; }} class Jill : ChildNode{public Jill(){this.Name = "Jill";}} class Marko : ChildNode{public Marko(){this.Name = "Marko";}}
首先,你要比较苹果和橘子.您首先需要比较开关类型与开启字符串,然后如果在类型vs比较字符串,然后比较获胜者.
其次,这是OO的设计目标.在支持OO的语言中,切换类型(任何类型)都是指代设计不良的代码气味.解决方案是使用抽象或虚拟方法(或类似的构造,取决于您的语言)从公共基础派生
例如.
class Node { public virtual void Action() { // Perform default action } } class Bob : Node { public override void Action() { // Perform action for Bill } } class Jill : Node { public override void Action() { // Perform action for Jill } }
然后,您只需调用childNode.Action(),而不是执行switch语句.
执行开关语句比if-else-if梯形图更快.这是由于编译器能够优化switch语句.对于if-else-if梯形图,代码必须按程序员确定的顺序处理每个if语句.但是,因为switch语句中的每个case都不依赖于早期的情况,所以编译器能够以提供最快执行的方式重新排序测试.
如果你已经完成了课程,我建议使用策略设计模式而不是switch或elseif.