哪个更快,基于字符串的switch语句还是基于类型的elseif语句?

81

假设我有两个选择,基于字符串比较来确定代码路径或者根据类型进行判断:

哪个更快,为什么?

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语句对于什么算作case非常奇怪。例如,它不允许您使用变量,只能使用被移动到主汇编代码中的常量。我假设它由于某些奇怪的事情而有此限制。如果它只是简单地转换为elseif语句(如一位评论者所说),那么为什么我们不能在case语句中使用变量呢?

注意事项: 我正在进行后优化。这种方法在应用程序的一个缓慢部分被调用多次


参见: https://dev59.com/HnVC5IYBdhLWcg3w4VVz https://dev59.com/RWw05IYBdhLWcg3wahNN https://dev59.com/ZGsz5IYBdhLWcg3w9syr https://dev59.com/5G855IYBdhLWcg3wMRVW https://dev59.com/qHVC5IYBdhLWcg3wbgdS - Mikhail Poda
https://dev59.com/61rUa4cB1Zd3GeqPkoIM http://stackoverflow.com/questions/6304815/why-is-this-switch-on-type-case-considered-confusing https://dev59.com/hVfUa4cB1Zd3GeqPDwfX https://dev59.com/s2LVa4cB1Zd3GeqPx5cP https://dev59.com/c0zSa4cB1Zd3GeqPqefO - Mikhail Poda
顺便提一下,你提供的两个选项并不等价。例如,如果 Marko 继承自 Bob,那么使用 is 运算符将会返回 true,即使 childNodeBobJillMarco 的子类也是如此。 - Matthijs Wessels
无论如何,我会选择if else方法。依赖于字符串表示可能会在未来出现问题。 - nawfal
2
FYI:C#7 现在允许对类型进行 switch/case,因此您不再需要将其编写为 if 语句。这可能是目前最好的选择。 - LanderV
显示剩余2条评论
24个回答

148

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 中的字符串映射到整数索引,然后基于该索引进行 switch。

它看起来像这样(想象一下比我打算打的更多的条目)

在包含 switch 语句的类中定义了一个静态字段,类型为 Dictionary<string, int>,并赋予一个混淆的名称,该名称位于“隐藏”的位置。

//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<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
    {
        //Unexpected type...
    }
}

这与Ted Elliot的建议类似,但使用运行时类型句柄而不是完整的类型对象可以避免通过反射加载类型对象的开销。

这是在我的机器上进行的一些快速计时:

测试3次迭代,使用5,000,000个数据元素(模式=随机)和5种类型 方法 时间 最优解的百分比 If/Else 179.67 100.00 TypeHandleDictionary 321.33 178.85 TypeDictionary 377.67 210.20 Switch 492.67 274.21
测试3次迭代,使用5,000,000个数据元素(模式=随机)和10种类型 方法 时间 最优解的百分比 If/Else 271.33 100.00 TypeHandleDictionary 312.00 114.99 TypeDictionary 374.33 137.96 Switch 490.33 180.71
测试3次迭代,使用5,000,000个数据元素(模式=随机)和15种类型 方法 时间 最优解的百分比 TypeHandleDictionary 312.00 100.00 If/Else 369.00 118.27 TypeDictionary 371.67 119.12 Switch 491.67 157.59
测试3次迭代,使用5,000,000个数据元素(模式=随机)和20种类型 方法 时间 最优解的百分比 TypeHandleDictionary 335.33 100.00 TypeDictionary 373.00 111.23 If/Else 462.67 137.97 Switch 490.33 146.22
测试3次迭代,使用5,000,000个数据元素(模式=随机)和25种类型 方法 时间 最优解的百分比 TypeHandleDictionary 319.33 100.00 TypeDictionary 371.00 116.18 Switch 483.00 151.25 If/Else 562.00 175.99
测试3次迭代,使用5,000,000个数据元素(模式=随机)和50种类型 方法 时间 最优解的百分比 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链中首先检查的类型组成,那么这个方法会快得多:
在5,000,000个数据元素(模式=UniformFirst)和50种类型的情况下,进行3次迭代测试
方法 时间 最佳状态百分比 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链的最后一个位置,将产生相反的效果:
在5,000,000个数据元素(模式=UniformLast)和50种类型的情况下,进行3次迭代测试
方法 时间 最佳状态百分比 TypeHandleDictionary 317.67 100.00 Switch 354.33 111.54 TypeDictionary 377.67 118.89 If/Else 1,907.67 600.52

如果您可以对输入进行一些假设,那么您可能会从混合方法中获得最佳性能,其中您对最常见的几种类型执行if/else检查,然后在这些失败时退回到基于字典的方法。


抱歉之前错过了这个回复。绝对值得置顶。 - Quibblesome
9
这可能是我在SO看过的最好的回答。天哪...点赞! - Ron L

19

首先,你在比较苹果和橙子。你需要先比较按类型开关和按字符串开关,然后再比较按类型和按字符串的if语句,最后比较胜者。

其次,这种情况是面向对象设计的典型应用。在支持面向对象的语言中,基于类型进行开关(任何类型)表明了代码设计存在问题。解决方法是从一个公共基类派生出来,并使用抽象或虚拟方法(或类似的结构,具体取决于你所使用的语言)

例如:

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
    }
}

然后,不是使用switch语句,而是调用childNode.Action()。


2
(除了可读性和可维护性之外)一个有趣的问题是,与另外两种方法相比,这种方法的表现如何。请注意:您还需要考虑选择和实例化“Node”实现(例如工厂)的部分的性能。 - Matthijs Wessels

18

我刚刚实现了一个简单的测试应用,并使用ANTS 4进行了分析。
规格:.Net 3.5 sp1在32位Windows XP上,代码在发布模式下构建。

300万次测试:

  • Switch语句:1.842秒
  • If语句:0.344秒。

此外,switch语句的结果表明(不出所料)较长的名称需要花费更长的时间。

100万次测试:

  • Bob:0.612秒。
  • Jill:0.835秒。
  • Marko: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";}}

2
这样做非常有意义,因为开关是使用字符串完成的,您必须考虑字符串转换开销。如果每个类都有一个枚举,会怎样呢? - Rick Minerich
4
“Bob” 更快是因为它更短还是因为它排在第一位? - Frank Szczerba

12

使用switch语句比if-else-if嵌套更快执行。这是由于编译器能够优化switch语句。在if-else-if嵌套的情况下,代码必须按照程序员确定的顺序逐个处理每个if语句。然而,在switch语句中,由于每个case不依赖先前的case,编译器能够以最快的方式重新排序测试。


然而,类型比较也更容易维护。-- 尽量避免过早优化。 - Nescio
1
这有助于说明所写的内容并不完全等同于执行的内容,但它误导地暗示了 IF 语句不能被优化。我不是优化专家,但我正在查看 Reflector 中的一个类,在其中一个带有只读变量的 If/ElseIf 语句在 IL 中的实现与 SWITCH 相同。 - Kevin
一个if-else-if结构确实可以被编译器转换为switch语句。为什么不能呢? - Tara

6
如果您已经创建了类,我建议使用策略设计模式而不是switch或elseif。

这是一个很好的建议!让对象自己决定它需要做什么。 - Bob King
那样做会更加优雅,避免所有这些麻烦。然而,要做到这一点需要在这个领域进行大量的重构,并且只能作为最后的手段使用。但总的来说,我同意你的看法。:D - Quibblesome

4

尝试使用枚举类型来表示每个对象,这样你可以快速、简便地在枚举类型中进行选择。


3
如果您要切换的类型是.NET原始类型,可以使用Type.GetTypeCode(Type),但如果它们是自定义类型,则所有类型都将返回TypeCode.Object。
具有委托或处理程序类的字典也可能起作用。
Dictionary<Type, HandlerDelegate> handlers = new Dictionary<Type, HandlerDelegate>();
handlers[typeof(Bob)] = this.HandleBob;
handlers[typeof(Jill)] = this.HandleJill;
handlers[typeof(Marko)] = this.HandleMarko;

handlers[childNode.GetType()](childNode);
/// ...

private void HandleBob(Node childNode) {
    // code to handle Bob
}

可爱。:) 你有没有想过委托调用会对性能产生任何连锁效应? - Quibblesome

3
一个 SWITCH 结构最初是用于整数数据的;它的目的是直接将参数用作“分发表”即指针表中的索引。因此,只需要进行一次测试,然后直接启动相关代码,而不是一系列的测试。
问题在于,它的使用已经泛化到“字符串”类型,显然不能用作索引,SWITCH 结构的所有优势都丧失了。
如果速度是您的目标,问题不在于您的代码,而在于您的数据结构。如果“名称”空间像您展示的那样简单,最好将其编码为整数值(例如,在创建数据时),并在“应用程序中多次运行的缓慢部分”中使用此整数。

2
我创建了一个小控制台来展示我的解决方案,以突出速度差异。由于证书版本在运行时太慢,而且重复的可能性很小,如果有的话,我的 switch 语句会失败(直到现在从未发生过)。我的唯一哈希扩展方法包含在下面的代码中。

Core 2 console app with output

我宁愿选择29个滴答声而不是695个滴答声,尤其是在使用关键代码时。
通过从给定数据库中获取一组字符串,您可以创建一个小应用程序,为您创建给定文件中的常量,以便在您的代码中使用。如果添加了值,则只需重新运行批处理,常量就会生成并被解决方案捕获。
  public static class StringExtention
    {
        public static long ToUniqueHash(this string text)
        {
            long value = 0;
            var array = text.ToCharArray();
            unchecked
            {
                for (int i = 0; i < array.Length; i++)
                {
                    value = (value * 397) ^ array[i].GetHashCode();
                    value = (value * 397) ^ i;
                }
                return value;
            }
        }
    }

    public class AccountTypes
    {

        static void Main()
        {
            var sb = new StringBuilder();

            sb.AppendLine($"const long ACCOUNT_TYPE = {"AccountType".ToUniqueHash()};");
            sb.AppendLine($"const long NET_LIQUIDATION = {"NetLiquidation".ToUniqueHash()};");
            sb.AppendLine($"const long TOTAL_CASH_VALUE = {"TotalCashValue".ToUniqueHash()};");
            sb.AppendLine($"const long SETTLED_CASH = {"SettledCash".ToUniqueHash()};");
            sb.AppendLine($"const long ACCRUED_CASH = {"AccruedCash".ToUniqueHash()};");
            sb.AppendLine($"const long BUYING_POWER = {"BuyingPower".ToUniqueHash()};");
            sb.AppendLine($"const long EQUITY_WITH_LOAN_VALUE = {"EquityWithLoanValue".ToUniqueHash()};");
            sb.AppendLine($"const long PREVIOUS_EQUITY_WITH_LOAN_VALUE = {"PreviousEquityWithLoanValue".ToUniqueHash()};");
            sb.AppendLine($"const long GROSS_POSITION_VALUE ={ "GrossPositionValue".ToUniqueHash()};");
            sb.AppendLine($"const long REQT_EQUITY = {"ReqTEquity".ToUniqueHash()};");
            sb.AppendLine($"const long REQT_MARGIN = {"ReqTMargin".ToUniqueHash()};");
            sb.AppendLine($"const long SPECIAL_MEMORANDUM_ACCOUNT = {"SMA".ToUniqueHash()};");
            sb.AppendLine($"const long INIT_MARGIN_REQ = { "InitMarginReq".ToUniqueHash()};");
            sb.AppendLine($"const long MAINT_MARGIN_REQ = {"MaintMarginReq".ToUniqueHash()};");
            sb.AppendLine($"const long AVAILABLE_FUNDS = {"AvailableFunds".ToUniqueHash()};");
            sb.AppendLine($"const long EXCESS_LIQUIDITY = {"ExcessLiquidity".ToUniqueHash()};");
            sb.AppendLine($"const long CUSHION = {"Cushion".ToUniqueHash()};");
            sb.AppendLine($"const long FULL_INIT_MARGIN_REQ = {"FullInitMarginReq".ToUniqueHash()};");
            sb.AppendLine($"const long FULL_MAINTMARGIN_REQ ={ "FullMaintMarginReq".ToUniqueHash()};");
            sb.AppendLine($"const long FULL_AVAILABLE_FUNDS = {"FullAvailableFunds".ToUniqueHash()};");
            sb.AppendLine($"const long FULL_EXCESS_LIQUIDITY ={ "FullExcessLiquidity".ToUniqueHash()};");
            sb.AppendLine($"const long LOOK_AHEAD_INIT_MARGIN_REQ = {"LookAheadInitMarginReq".ToUniqueHash()};");
            sb.AppendLine($"const long LOOK_AHEAD_MAINT_MARGIN_REQ = {"LookAheadMaintMarginReq".ToUniqueHash()};");
            sb.AppendLine($"const long LOOK_AHEAD_AVAILABLE_FUNDS = {"LookAheadAvailableFunds".ToUniqueHash()};");
            sb.AppendLine($"const long LOOK_AHEAD_EXCESS_LIQUIDITY = {"LookAheadExcessLiquidity".ToUniqueHash()};");
            sb.AppendLine($"const long HIGHEST_SEVERITY = {"HighestSeverity".ToUniqueHash()};");
            sb.AppendLine($"const long DAY_TRADES_REMAINING = {"DayTradesRemaining".ToUniqueHash()};");
            sb.AppendLine($"const long LEVERAGE = {"Leverage".ToUniqueHash()};");
            Console.WriteLine(sb.ToString());

            Test();    
        }    

        public static void Test()
        {
            //generated constant values
            const long ACCOUNT_TYPE = -3012481629590703298;
            const long NET_LIQUIDATION = 5886477638280951639;
            const long TOTAL_CASH_VALUE = 2715174589598334721;
            const long SETTLED_CASH = 9013818865418133625;
            const long ACCRUED_CASH = -1095823472425902515;
            const long BUYING_POWER = -4447052054809609098;
            const long EQUITY_WITH_LOAN_VALUE = -4088154623329785565;
            const long PREVIOUS_EQUITY_WITH_LOAN_VALUE = 6224054330592996694;
            const long GROSS_POSITION_VALUE = -7316842993788269735;
            const long REQT_EQUITY = -7457439202928979430;
            const long REQT_MARGIN = -7525806483981945115;
            const long SPECIAL_MEMORANDUM_ACCOUNT = -1696406879233404584;
            const long INIT_MARGIN_REQ = 4495254338330797326;
            const long MAINT_MARGIN_REQ = 3923858659879350034;
            const long AVAILABLE_FUNDS = 2736927433442081110;
            const long EXCESS_LIQUIDITY = 5975045739561521360;
            const long CUSHION = 5079153439662500166;
            const long FULL_INIT_MARGIN_REQ = -6446443340724968443;
            const long FULL_MAINTMARGIN_REQ = -8084126626285123011;
            const long FULL_AVAILABLE_FUNDS = 1594040062751632873;
            const long FULL_EXCESS_LIQUIDITY = -2360941491690082189;
            const long LOOK_AHEAD_INIT_MARGIN_REQ = 5230305572167766821;
            const long LOOK_AHEAD_MAINT_MARGIN_REQ = 4895875570930256738;
            const long LOOK_AHEAD_AVAILABLE_FUNDS = -7687608210548571554;
            const long LOOK_AHEAD_EXCESS_LIQUIDITY = -4299898188451362207;
            const long HIGHEST_SEVERITY = 5831097798646393988;
            const long DAY_TRADES_REMAINING = 3899479916235857560;
            const long LEVERAGE = 1018053116254258495;

            bool found = false;
            var sValues = new string[] {
              "AccountType"
              ,"NetLiquidation"
              ,"TotalCashValue"
              ,"SettledCash"
              ,"AccruedCash"
              ,"BuyingPower"
              ,"EquityWithLoanValue"
              ,"PreviousEquityWithLoanValue"
              ,"GrossPositionValue"
              ,"ReqTEquity"
              ,"ReqTMargin"
              ,"SMA"
              ,"InitMarginReq"
              ,"MaintMarginReq"
              ,"AvailableFunds"
              ,"ExcessLiquidity"
              ,"Cushion"
              ,"FullInitMarginReq"
              ,"FullMaintMarginReq"
              ,"FullAvailableFunds"
              ,"FullExcessLiquidity"
              ,"LookAheadInitMarginReq"
              ,"LookAheadMaintMarginReq"
              ,"LookAheadAvailableFunds"
              ,"LookAheadExcessLiquidity"
              ,"HighestSeverity"
              ,"DayTradesRemaining"
              ,"Leverage"
            };

            long t1, t2;
            var sw = System.Diagnostics.Stopwatch.StartNew();
            foreach (var name in sValues)
            {
                switch (name)
                {
                    case "AccountType": found = true; break;
                    case "NetLiquidation": found = true; break;
                    case "TotalCashValue": found = true; break;
                    case "SettledCash": found = true; break;
                    case "AccruedCash": found = true; break;
                    case "BuyingPower": found = true; break;
                    case "EquityWithLoanValue": found = true; break;
                    case "PreviousEquityWithLoanValue": found = true; break;
                    case "GrossPositionValue": found = true; break;
                    case "ReqTEquity": found = true; break;
                    case "ReqTMargin": found = true; break;
                    case "SMA": found = true; break;
                    case "InitMarginReq": found = true; break;
                    case "MaintMarginReq": found = true; break;
                    case "AvailableFunds": found = true; break;
                    case "ExcessLiquidity": found = true; break;
                    case "Cushion": found = true; break;
                    case "FullInitMarginReq": found = true; break;
                    case "FullMaintMarginReq": found = true; break;
                    case "FullAvailableFunds": found = true; break;
                    case "FullExcessLiquidity": found = true; break;
                    case "LookAheadInitMarginReq": found = true; break;
                    case "LookAheadMaintMarginReq": found = true; break;
                    case "LookAheadAvailableFunds": found = true; break;
                    case "LookAheadExcessLiquidity": found = true; break;
                    case "HighestSeverity": found = true; break;
                    case "DayTradesRemaining": found = true; break;
                    case "Leverage": found = true; break;
                    default: found = false; break;
                }

                if (!found)
                    throw new NotImplementedException();
            }
            t1 = sw.ElapsedTicks;
            sw.Restart();
            foreach (var name in sValues)
            {
                switch (name.ToUniqueHash())
                {
                    case ACCOUNT_TYPE:
                        found = true;
                        break;
                    case NET_LIQUIDATION:
                        found = true;
                        break;
                    case TOTAL_CASH_VALUE:
                        found = true;
                        break;
                    case SETTLED_CASH:
                        found = true;
                        break;
                    case ACCRUED_CASH:
                        found = true;
                        break;
                    case BUYING_POWER:
                        found = true;
                        break;
                    case EQUITY_WITH_LOAN_VALUE:
                        found = true;
                        break;
                    case PREVIOUS_EQUITY_WITH_LOAN_VALUE:
                        found = true;
                        break;
                    case GROSS_POSITION_VALUE:
                        found = true;
                        break;
                    case REQT_EQUITY:
                        found = true;
                        break;
                    case REQT_MARGIN:
                        found = true;
                        break;
                    case SPECIAL_MEMORANDUM_ACCOUNT:
                        found = true;
                        break;
                    case INIT_MARGIN_REQ:
                        found = true;
                        break;
                    case MAINT_MARGIN_REQ:
                        found = true;
                        break;
                    case AVAILABLE_FUNDS:
                        found = true;
                        break;
                    case EXCESS_LIQUIDITY:
                        found = true;
                        break;
                    case CUSHION:
                        found = true;
                        break;
                    case FULL_INIT_MARGIN_REQ:
                        found = true;
                        break;
                    case FULL_MAINTMARGIN_REQ:
                        found = true;
                        break;
                    case FULL_AVAILABLE_FUNDS:
                        found = true;
                        break;
                    case FULL_EXCESS_LIQUIDITY:
                        found = true;
                        break;
                    case LOOK_AHEAD_INIT_MARGIN_REQ:
                        found = true;
                        break;
                    case LOOK_AHEAD_MAINT_MARGIN_REQ:
                        found = true;
                        break;
                    case LOOK_AHEAD_AVAILABLE_FUNDS:
                        found = true;
                        break;
                    case LOOK_AHEAD_EXCESS_LIQUIDITY:
                        found = true;
                        break;
                    case HIGHEST_SEVERITY:
                        found = true;
                        break;
                    case DAY_TRADES_REMAINING:
                        found = true;
                        break;
                    case LEVERAGE:
                        found = true;
                        break;
                    default:
                        found = false;
                        break;
                }

                if (!found)
                    throw new NotImplementedException();
            }
            t2 = sw.ElapsedTicks;
            sw.Stop();
            Console.WriteLine($"String switch:{t1:N0} long switch:{t2:N0}");
            var faster = (t1 > t2) ? "Slower" : "faster";
            Console.WriteLine($"String switch: is {faster} than long switch: by {Math.Abs(t1-t2)} Ticks");
            Console.ReadLine();

        }

2
我不确定该如何更快速,正确的设计是采用多态性。
interface INode
{
    void Action;
}

class Bob : INode
{
    public void Action
    {

    }
}

class Jill : INode
{
    public void Action
    {

    }
}

class Marko : INode
{
    public void Action
    {

    }
}

//Your function:
void Do(INode childNode)
{
    childNode.Action();
}

了解您的switch语句执行情况将有助于更好地理解。如果您的函数与类型无关,则可以在每种类型上定义一个枚举。

enum NodeType { Bob, Jill, Marko, Default }

interface INode
{
    NodeType Node { get; };
}

class Bob : INode
{
    public NodeType Node { get { return NodeType.Bob; } }
}

class Jill : INode
{
    public NodeType Node { get { return NodeType.Jill; } }
}

class Marko : INode
{
    public NodeType Node { get { return NodeType.Marko; } }
}

//Your function:
void Do(INode childNode)
{
    switch(childNode.Node)
    {
        case Bob:
          break;
        case Jill:
          break;
        case Marko:
          break;
        Default:
          throw new ArgumentException();
    }
}

我假设这种方法比问题中的两种方法都要快。如果纳秒对您很重要,您可能需要尝试抽象类路由请注意保留HTML标签。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接