什么是更快,打开字符串或elseif类型?

问题描述 投票:73回答:23

假设我可以选择在字符串比较的基础上识别代码路径,或者如果确定类型:

哪个更快,为什么?

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语句中使用变量?

警告:我是后期优化的。在app的缓慢部分中多次调用此方法。

c# performance
23个回答
124
投票

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();
}

一旦字典中的项目列表“足够大”,编译器将自动创建一个内部字典,该字典从交换机中的字符串映射到整数索引,然后基于该索引切换。

它看起来像这样(想象更多的条目比我打扰打字)

静态字段在“隐藏”位置定义,该位置与包含Dictionary<string, int>类型的switch语句的类相关联,并且给出了错误的名称

//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所建议的,但运行时类型句柄而不是完整类型对象的使用避免了通过反射加载类型对象的开销。

以下是我机器上的一些快速计时:

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检查最常见的几种类型,然后如果这些方法失败则回退到字典驱动方法。


2
投票

switch()将编译为相当于一组else ifs的代码。字符串比较将比类型比较慢得多。


2
投票

我记得在几本参考书中读到if / else分支比switch语句更快。然而,对Blackwasp的一些研究表明,switch语句实际上更快:http://www.blackwasp.co.uk/SpeedTestIfElseSwitch.aspx

实际上,如果你比较典型的3到10个(左右)语句,我会严重怀疑使用其中一个或那个有任何实际的性能提升。

正如克里斯已经说过的那样,为了便于阅读:What is quicker, switch on string or elseif on type?


2
投票

我认为这里的主要性能问题是,在switch块中,你比较字符串,而在if-else块中,你检查类型......那两个不一样,因此,我会说你“将土豆与香蕉比较”。

我首先要比较一下:

switch(childNode.Name)
{
    case "Bob":
        break;
    case "Jill":
        break;
    case "Marko":
      break;
}

if(childNode.Name == "Bob")
{}
else if(childNode.Name == "Jill")
{}
else if(childNode.Name == "Marko")
{}

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();
    }
}

我认为这必须比两种方法都快。您可能想尝试抽象类路由if nanoseconds does matter for you


2
投票

我创建了一个小控制台来显示我的解决方案,只是为了突出速度差异。我使用了不同的字符串哈希算法,因为证书版本在运行时对我来说很慢并且重复不太可能,如果是这样,我的switch语句将失败(直到现在才发生)。我唯一的哈希扩展方法包含在下面的代码中。

Core 2 console app with output

我会随时使用超过695个刻度的29个刻度,特别是在使用关键代码时。

使用来自给定数据库的一组字符串,您可以创建一个小应用程序来在给定文件中创建常量以供您在代码中使用,如果添加了值,则只需重新运行批处理并生成常量并通过解决方案。

  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();

        }

0
投票

字符串比较将始终完全依赖于运行时环境(除非字符串是静态分配的,尽管需要将它们相互比较是有争议的)。但是,类型比较可以通过动态或静态绑定来完成,无论哪种方式,它对运行时环境比比较字符串中的单个字符更有效。


0
投票

当然,String上的开关会编译为String比较(每种情况一个),这比类型比较慢(并且比用于switch / case的典型整数比较慢)?


0
投票

三个想法:

1)如果你要根据对象的类型做一些不同的事情,那么将这种行为转移到这些类中可能是有意义的。然后,而不是切换或if-else,你只需要调用childNode.DoSomething()。

2)比较类型将比字符串比较快得多。

3)在if-else设计​​中,您可以利用重新排序测试。如果“Jill”对象组成了90%的对象,请先测试它们。


0
投票

你对交换机的一个问题是使用字符串,比如“Bob”,这将在编译的代码中引起更多的循环和行。生成的IL必须声明一个字符串,将其设置为“Bob”,然后在比较中使用它。因此,考虑到这一点,您的IF语句将运行得更快。

PS。永旺的例子不会起作用,因为你无法打开类型。 (不,我不知道究竟是为什么,但我们已经尝试过它不起作用。它与变量类型有关)

如果你想测试它,只需构建一个单独的应用程序并构建两个简单的方法,它们执行上面所写的操作并使用类似Ildasm.exe的方法来查看IL。您会注意到IF语句Method的IL中的行数少得多。

Ildasm附带Visual Studio ......

ILDASM页面 - http://msdn.microsoft.com/en-us/library/f7dy01k1(VS.80).aspx

ILDASM教程 - http://msdn.microsoft.com/en-us/library/aa309387(VS.71).aspx


0
投票

请记住,探查器是你的朋友。任何猜测都是浪费大部分时间的时间。顺便说一下,我对JetBrains的dotTrace探测器有很好的体验。


18
投票

我刚刚实现了一个快速测试应用程序并用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";}}

0
投票

切换字符串基本上被编译成if-else-if梯形图。尝试反编译一个简单的。在任何情况下,测试字符串equailty应该更便宜,因为它们是实习的,所需要的只是参考检查。在可维护性方面做有意义的事情;如果你要编译字符串,请执行字符串切换。如果您根据类型进行选择,则类型阶梯更合适。


0
投票

我有点不同,你打开的字符串将是常量,所以你可以在编译时预测值。

在你的情况下,我使用哈希值,这是一个int开关,你有2个选项,使用编译时常量或在运行时计算。

//somewhere in your code
static long _bob = "Bob".GetUniqueHashCode();
static long _jill = "Jill".GetUniqueHashCode();
static long _marko = "Marko".GeUniquetHashCode();

void MyMethod()
{
   ...
   if(childNode.Tag==0)
      childNode.Tag= childNode.Name.GetUniquetHashCode()

   switch(childNode.Tag)
   {
       case _bob :
        break;
       case _jill :
         break;
       case _marko :
        break;
   }
}

GetUniquetHashCode的扩展方法可以是这样的:

public static class StringExtentions
    {
        /// <summary>
        /// Return unique Int64 value for input string
        /// </summary>
        /// <param name="strText"></param>
        /// <returns></returns>
        public static Int64 GetUniquetHashCode(this string strText)
        {
            Int64 hashCode = 0;
            if (!string.IsNullOrEmpty(strText))
            {
                //Unicode Encode Covering all character-set
                byte[] byteContents = Encoding.Unicode.GetBytes(strText);
                System.Security.Cryptography.SHA256 hash =  new System.Security.Cryptography.SHA256CryptoServiceProvider();
                byte[] hashText = hash.ComputeHash(byteContents);
                //32Byte hashText separate
                //hashCodeStart = 0~7  8Byte
                //hashCodeMedium = 8~23  8Byte
                //hashCodeEnd = 24~31  8Byte
                //and Fold
                Int64 hashCodeStart = BitConverter.ToInt64(hashText, 0);
                Int64 hashCodeMedium = BitConverter.ToInt64(hashText, 8);
                Int64 hashCodeEnd = BitConverter.ToInt64(hashText, 24);
                hashCode = hashCodeStart ^ hashCodeMedium ^ hashCodeEnd;
            }
            return (hashCode);
        }


    }

这段代码的来源发布了here请注意,使用Cryptography很慢,你通常会在应用程序启动时预热支持的字符串,我这样做是将它们保存在静态字段中,因为它们不会改变,也不是实例相关的。请注意,我设置了节点对象的标记值,我可以使用任何属性或添加一个属性,只需确保它们与实际文本同步。

我在低延迟系统上工作,我的所有代码都是一串命令:value,command:value ....

现在命令都称为64位整数值,因此这样切换可以节省一些CPU时间。


0
投票

我只是在这里阅读答案列表,并想分享this benchmark test,它将switch构造与if-else和三元?算子进行比较。

我喜欢that post它不仅比较单左构造(例如,if-else),而且比较双层和三层构造(例如,if-else-if-else)。

根据结果​​,if-else构造在8/9测试案例中是最快的; switch构造在5/9测试案例中并列最快。

因此,如果你正在寻找速度if-else似乎是最快的方式去。


-1
投票

我可能会遗漏一些东西,但是你不能在类型而不是字符串上执行switch语句吗?那是,

switch(childNode.Type)
{
case Bob:
  break;
case Jill:
  break;
case Marko:
  break;
}

17
投票

首先,你要比较苹果和橘子。您首先需要比较开关类型与开启字符串,然后如果在类型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语句。


12
投票

执行开关语句比if-else-if梯形图更快。这是由于编译器能够优化switch语句。对于if-else-if梯形图,代码必须按程序员确定的顺序处理每个if语句。但是,因为switch语句中的每个case都不依赖于早期的情况,所以编译器能够以提供最快执行的方式重新排序测试。


6
投票

如果你已经完成了课程,我建议使用策略设计模式而不是switch或elseif。


4
投票

尝试使用每个对象的枚举,您可以快速轻松地打开枚举。


3
投票

除非您已经写过并发现性能问题,否则我不会担心哪个更快。选择一个更具可读性的产品。请记住,“过早优化是所有邪恶的根源。” - 唐纳德克努特


3
投票

SWITCH结构最初用于整数数据;它的目的是将参数直接用作“调度表”(一个指针表)的索引。因此,将进行单个测试,然后直接启动相关代码,而不是一系列测试。

这里的困难在于它的使用被推广为“字符串”类型,显然不能用作索引,并且SWITCH结构的所有优点都会丢失。

如果速度是您的预期目标,问题不是您的代码,而是您的数据结构。如果“name”空间与显示它一样简单,最好将其编码为整数值(例如,创建数据时),并在“应用程序的缓慢部分中多次使用”中使用此整数。


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
}
© www.soinside.com 2019 - 2024. All rights reserved.