什么更快,打开字符串或其他类型?


77

可以说,我可以选择基于字符串比较来确定要采用的代码路径,也可以选择类型:

哪个更快,为什么?

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

警告:我正在优化。这种方法被称为许多在应用程序的缓慢一部分倍。


我想知道为什么swtich语句也不允许变量。
Metro Smurf

fyi-他们在VB中执行,但不在C#中执行。
Nescio



2
仅供参考:C#7现在允许切换/转换类型,因此您不必像声明一样编写它。这可能是目前最好的选择。
LanderV '17

Answers:


138

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>并给定名称错误的类相关联

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

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

使用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检查,然后在失败的情况下退回到字典驱动的方法。


抱歉,我错过了此回复。绝对值得成为顶级。
09年

8
这可能是我在SO上看到的东西的最佳答案。天哪,竖起大拇指!
罗恩L

19

首先,您要比较苹果和橙子。您首先需要比较启用类型与启用字符串,然后比较类型与字符串,然后比较获胜者。

其次,这是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
    }
}

然后,您无需调用switch语句,而只需调用childNode.Action()


2
(除了可读性和可维护性之外)一个有趣的问题是,与其他两种方法相比,该方法的性能如何。注意:您还应该考虑Node选择并实例化实现的部分(例如Factory)的性能。
Matthijs Wessels,2012年

18

我刚刚实现了一个快速测试应用程序,并使用ANTS 4对其进行了分析。
规格:32位Windows XP中的.Net 3.5 sp1,代码以发布模式构建。

300万次测试:

  • 切换:1.842秒
  • 如果:0.344秒。

此外,switch语句的结果显示(毫不奇怪)较长的名称花费较长的时间。

一百万次测试

  • 鲍勃: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";}}

2
这非常有意义,因为切换是通过字符串完成的,因此您必须考虑字符串转换的开销。如果每个班级都有一个枚举怎么办?
Rick Minerich

4
是因为“鲍勃”(Bob)较短还是因为其首位,速度更快?
Frank Szczerba

12

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


但是,类型比较也更容易维护。-尽量不要过早优化。
Nescio

1
这有助于说出所写的不完全是所执行的内容,但在提示无法优化IF时会产生误导。我不是优化专家,但是我正在看Reflector中的一个类,其中在IL中实现了一个带readonly变量的If / ElseIf和SWITCH一样。
凯文·克鲁姆利

编译器确实可以将if-else-if构造转换为switch语句。为什么不能呢?
塔拉

6

如果您已经制作了这些类,则建议您使用策略设计模式,而不要使用switch或elseif。


这是一个很好的建议!让对象自己决定需要做什么。
鲍勃·金

这样会更加优雅,并且省去了所有麻烦。但是,这样做将需要在该区域进行大量重构,并且只能用作最后的手段。但我总体上同意你的看法。:D
Quibblesome


3

除非您已经写过这篇文章,并且发现您有性能问题,否则我不会担心哪个更快。选择可读性更强的一种。请记住,“过早的优化是万恶之源。” -唐纳德​​·努斯


1
除非我们没有过早地进行优化。我们正在进行优化。抱歉,但-1。
Quibblesome

1
这里的答案不仅是为了提问者的利益,而是给以后出现的任何人的。人们需要意识到,出于性能方面的考虑而提前做出决定并不是最好的方法。
克里斯·厄普彻奇

哦,太好了,现在指出该问题无关的答案高于其他所有实际尝试回答该问题的答案。同行的人!:P
Quibblesome

@Quarrelsome:提防过早优化与任何优化问题都有关,除非您指定已经分析了代码并发现它太慢。将其放在您的问题中,我的答案可能会停止被投票。
克里斯·厄普彻奇

1
考虑到过早的优化是多么普遍,是的。
克里斯·厄普彻奇

3

SWITCH构造最初用于整数数据。目的是将参数直接用作“调度表”(指针表)的索引。这样,将只有一个测试,然后直接启动到相关代码,而不是一系列测试。

此处的困难在于,它的使用已被普遍化为“字符串”类型,显然不能用作索引,并且SWITCH构造的所有优势都丢失了。

如果速度是您的预期目标,那么问题不在于代码,而在于数据结构。如果“名称”空间如您所显示的那么简单,则最好将其编码为一个整数值(例如,创建数据时),并在“应用程序速度较慢的部分中多次使用”该整数。


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

2

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


CLR不会在switch语句中执行任何时髦的把戏吗?为什么它会迫使您仅将常量用作case语句,而不是仅将变量转换为else ifs的变量?
Quibblesome

如果switch语句使用基本类型,则CLR将执行时髦的技巧,但是,在每种情况下,都需要进行字符串比较,因此优化的余地很小。
moonshadow

1
C#不会将基于字符串的开关编译为字符串比较。由于大小写标签必须是文字,因此使用了一些技巧,例如,对切换的变量进行求值,打开哈希码,然后检查对象身份(由于求值而起作用)以确保匹配正确。
Michael Burr

哦,现在这很有趣。因此,CLR会为其他类型的字符串带来例外吗?那么,这是否等于ifs?
Quibblesome

2

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

实际上,如果您比较的是典型的3到10(或大约10条)语句,我严重怀疑使用其中的一种会带来真正的性能提升。

就像克里斯已经说过的那样,为了提高可读性: 什么更快,打开字符串或其他类型?


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")
{}

1
嗨,谢谢您的回复!这实际上是我所遇到的情况。我们可以使用唯一的ID(它是字符串)或对象类型来区分这些对象。
Quibblesome

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

我认为这必须比所讨论的两种方法都要快。如果纳秒确实对您很重要,则您可能想尝试抽象类路由。


2

我创建了一个小控制台来展示我的解决方案,只是为了强调速度差异。我使用了不同的字符串哈希算法,因为证书版本会使我在运行时变慢,并且不太可能重复,如果这样我的switch语句将失败(到目前为止从未发生过)。我独特的哈希扩展方法包含在下面的代码中。

带有输出的Core 2控制台应用

我会在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)如果您要根据对象的类型执行不同的操作,则可以将该行为移入那些类中。然后,您只需调用childNode.DoSomething()来代替switch或if-else。

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

3)在if-else设计​​中,您可以利用对测试进行重新排序的优势。如果“吉尔”物体占通过该物体的物体的90%,请首先对其进行测试。


0

开关的问题之一是使用字符串,例如“ Bob”,这将在编译后的代码中引起更多的循环和更多行。生成的IL将必须声明一个字符串,将其设置为“ Bob”,然后在比较中使用它。因此,请记住,您的IF语句将运行得更快。

PS。Aeon的示例无法正常工作,因为您无法打开“类型”。(不,我不知道为什么会这样,但是我们已经尝试了它,但是它不起作用。它与变量类型有关)

如果要对此进行测试,只需构建一个单独的应用程序并构建两个简单的方法即可完成上面所述的操作,并使用Ildasm.exe之类的工具查看IL。您会发现IF语句Method的IL中的行少了很多。

Ildasm随附VisualStudio ...

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

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



0

接通字符串基本上被编译为if-else-if阶梯。尝试反编译一个简单的。在任何情况下,测试字符串的适用性都应该便宜一些,因为它们是经过检查的,所需要的只是参考检查。在可维护性方面做有意义的事情;如果要压缩字符串,请进行字符串切换。如果基于类型进行选择,则更适合使用类型阶梯。


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


    }

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

我在低延迟系统上工作,我所有的代码都是以command:value,command:value ...的字符串形式出现的。

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


0

我只是在这里阅读答案列表,并想分享这个基准测试该测试switch结构与if-else和三元?运算符进行了比较。

我喜欢这篇文章的地方是,它不仅比较了左单构造(例如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;
}

不,切换操作不适用于类型等对象。仅“整数类型”
Quibblesome

字符串不是整数类型!
moonshadow

然后,为什么框架允许字符串而不是带有编译器错误的类型:“需要整数类型的值”。即使字符串不是整数类型,允许您使用字符串也只是一个小技巧?
Quibblesome

哦 好吧,很抱歉:)我不了解C#,允许将有效的条件作为if条件作为转换条件似乎合乎逻辑。
永世

@Quibblesome确实是一个把戏。开关盒可以处理字符串,尽管它们不是整数类型
nawfal
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.