Greg的个人资料结果对于他所涵盖的确切场景非常有用,但是有趣的是,当考虑许多不同因素(包括所比较类型的数量,相对数据的频率和基础数据中的任何模式)时,不同方法的相对成本发生了巨大变化。 。
简单的答案是,没有人能告诉您在特定情况下的性能差异,您将需要在自己的系统中以各种方式衡量性能,以获得准确的答案。
If / Else链是少数类型比较的一种有效方法,或者您可以可靠地预测哪种类型的类型将构成您看到的大多数类型的比较。该方法的潜在问题在于,随着类型数量的增加,必须执行的比较数量也随之增加。
如果我执行以下命令:
int value = 25124;
if(value == 0) ...
else if (value == 1) ...
else if (value == 2) ...
...
else if (value == 25124) ...
在输入正确的程序段之前,必须先评估每个先前的条件。另一方面
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<string, int>
并给定名称错误的类相关联
if(theDictionary == null) {
theDictionary = new Dictionary<string,int>();
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<RuntimeTypeHandle, NodeHandler> TypeHandleSwitcher = CreateSwitcher();
private static Dictionary<RuntimeTypeHandle, NodeHandler> CreateSwitcher()
{
var ret = new Dictionary<RuntimeTypeHandle, NodeHandler>();
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
{
}
}
这与Ted Elliot的建议类似,但是使用运行时类型句柄而不是完整类型对象避免了通过反射加载类型对象的开销。
以下是我机器上的一些快速计时:
使用5,000,000个数据元素(mode = Random)和5种类型测试3次迭代
方法时间最佳百分比
如果/否则179.67 100.00
类型句柄字典321.33 178.85
类型字典377.67 210.20
开关492.67 274.21
使用5,000,000个数据元素(mode = Random)和10种类型测试3次迭代
方法时间最佳百分比
如果/否则271.33 100.00
类型句柄词典312.00 114.99
类型字典374.33 137.96
开关490.33 180.71
使用5,000,000个数据元素(mode = Random)和15种类型测试3次迭代
方法时间最佳百分比
类型句柄字典312.00 100.00
如果/否则369.00 118.27
类型字典371.67 119.12
开关491.67 157.59
使用5,000,000个数据元素(mode = Random)和20种类型测试3次迭代
方法时间最佳百分比
类型句柄字典335.33 100.00
类型字典373.00 111.23
如果/否则462.67 137.97
开关490.33 146.22
使用5,000,000个数据元素(mode = Random)和25种类型测试3次迭代
方法时间最佳百分比
类型句柄词典319.33 100.00
类型字典371.00 116.18
开关483.00 151.25
如果/其他562.00 175.99
使用5,000,000个数据元素(mode = Random)和50种类型测试3次迭代
方法时间最佳百分比
类型句柄词典319.67 100.00
类型字典376.67 117.83
开关453.33 141.81
如果/否则1,032.67 323.04
至少在我的机器上,当用作方法输入的类型的分布是随机的时,对于超过15种不同类型的任何类型,类型句柄字典方法都击败了其他方法。
另一方面,如果输入完全由if / else链中首先检查的类型组成,则该方法要快得多:
使用5,000,000个数据元素(mode = UniformFirst)和50种类型测试3次迭代
方法时间最佳百分比
如果/否则39.00 100.00
类型句柄字典317.33 813.68
类型字典396.00 1,015.38
开关403.00 1,033.33
相反,如果输入始终是if / else链中的最后一件事,则会产生相反的效果:
使用5,000,000个数据元素(mode = UniformLast)和50种类型测试3次迭代
方法时间最佳百分比
类型句柄字典317.67 100.00
开关354.33 111.54
类型字典377.67 118.89
如果/其他1,907.67 600.52
如果您可以对输入做出一些假设,则可以从混合方法中获得最佳性能,在该方法中,对最常见的几种类型执行if / else检查,然后在失败的情况下退回到字典驱动的方法。