代码之家  ›  专栏  ›  技术社区  ›  code4life

将对象转换为双精度对象的最快方法?

  •  14
  • code4life  · 技术社区  · 14 年前

    将对象转换为双精度对象的最快方法是什么?我现在正在写一段代码,上面写着:

    var d = double.TryParse(o.ToString(), out d);  // o is the Object...
    

    最初的想法是把这个改写为

    var d = Convert.ToDouble(o);
    

    但那会更快吗?

    编辑: 除了运行配置文件(顺便说一下,我强烈推荐 JetBrains点迹 对于任何开发人员),我都运行了Reflector,这有助于我想出以下内容(或多或少是代码的相关部分):

    if (o is IConvertible)
    {
        d = ((IConvertible)o).ToDouble(null);
    }
    else
    {
        d = 0d;
    }
    

    原始代码 double.TryParse() 在140ms内执行。新代码在34ms内执行。我几乎可以肯定这是我应该采用的优化路径,但是在我这样做之前,有人看到我的“优化”代码有什么问题吗?提前感谢您的反馈!

    5 回复  |  直到 8 年前
        1
  •  14
  •   Jim Balter    8 年前

    你必须做很多这样的事情,才能有意义的花时间在这上面。但是,我不是来判断:

    所以,你的代码是:

    if (o is IConvertible)
    {
        d = ((IConvertible)o).ToDouble(null);
    }
    else
    {
        d = 0d;
    }
    

    我想知道你用这个会不会更好

    IConvertible convert = o as IConvertible;
    
    if (convert != null)
    {
      d = convert.ToDouble(null);
    }
    else
    {
      d = 0d;
    }
    

    省去你的双重角色。

        2
  •  5
  •   Brian Gideon    14 年前

    我尝试了以下方法。

    • 三重解析
    • 双解析
    • 转换.todouble

    我使用了以下代码。

    public static void Main()
    {
        string text = "3.14";
        var timer = new Stopwatch();
        timer.Start();
        for (int i = 0; i < 10000000; i++)
        {
            double d;
            d = Convert.ToDouble(text);
            //double.TryParse(text, out d);
            //d = double.Parse(text);
        }
        timer.Stop();
        Console.WriteLine("Time=" + timer.Elapsed.ToString());
        Console.ReadLine();
    }
    

    在我的机器上,我看到了这些结果。我平均跑了3次。

    • double.triparse=4.45秒
    • double.parse=4.45秒
    • convert.todouble=4.75秒

    当然,我使用了一个可转换的字符串。如果字符串不可转换,那么我强烈怀疑 double.TryParse 将是最快的远射。

        3
  •  3
  •   Adam Houldsworth    14 年前

    使用system.diagnostics.stopwatch创建一个小的测试应用程序,并查看哪个应用程序的速度更快。尽管我会争辩说这不会有什么意义。我愿意去 Convert.ToDouble 纯粹是为了可读性。

        4
  •  1
  •   Chris B.    14 年前

    首先,如果您真的想知道哪个更快,您应该编写一个快速测试(使用您希望处理的数据)并为每个选项计时。不知道什么 o 是(或可能是)很难判断。我怀疑你不会看到太大的不同。

    其次,除非您在 极其 代码的时间关键部分,调用它数千次启动,我怀疑它真的很重要。写一个好的,干净的代码, 然后 优化。

        5
  •  0
  •   gullymiles    9 年前

    根据o是什么类型的,您可以尝试做几个不同的事情。它可能是

    a)盒装双人床,您只需将其解开:

    object o = 53.2;
    double d = (double)o;
    

    b)其他一些类型、值或引用,具有到Double的某些转换(实现IConvertible.ToDouble()),您希望使用

    object o = 53.2M; // a System.Decimal
    double d = Convert.ToDouble(o);
    

    c)具有默认字符串表示形式的东西,可以解析为双精度字符串。

    object o = "53.2";
    double d;
    bool convertedOK = double.TryParse(o.ToString(), out d);
    

    从某种意义上说,选项C是最长的循环方式;您将获取对象,请求其字符串表示,然后尝试解析该字符串以获得双精度值。如果您不需要这样做,这是笨拙的,并且在40000个调用的示例中,它将创建并丢弃40000个字符串…

    如果您知道您的对象总是包含实现到double转换的内容,那么您可以跳过所有这些内容,选择选项b。如果您知道您的对象只是一个装箱的double,那么选择最简单的选项(a)将其取消装箱。

    如果你真的不知道O会是什么的话,也许沿着这些线的某些东西会对你有用?

    double d = (o is double) ? (double)o
        : (o is IConvertible) ? (o as IConvertible).ToDouble(null)
        : double.Parse(o.ToString());
    

    (注意:如果o包含实现IConvertible但无法转换为Double的内容,或者其字符串表示形式无法解析为Double,则此操作不起作用)

    我没有提到相对速度,但是如果拆箱没有比转换为字符串然后解析(除非优化器非常聪明)快得多,我会很惊讶的。

    在LinqPad中使用.NET秒表进行的一次快速测试显示出很大的差异。

    IEnumerable<object> myData = new List<object>() { "53.2", 53.2M, 53.2D };
    const int iterations = 10000000;
    var sw = new Stopwatch();
    var results = new List<string>();
    
    foreach (var o in myData)
    {
        sw.Reset();
        sw.Start();
    
        for (var i=0; i < iterations; i++)
        {
            double d = (o is double) ? (double)o
                : (o is IConvertible) ? (o as IConvertible).ToDouble(null)
                : double.Parse(o.ToString());
        }
    
        sw.Stop();
    
        results.Add($"{o.GetType()}: {iterations} iterations took {sw.ElapsedMilliseconds}ms");
    }
    
    results.Dump();
    

    在我的电脑上给出以下结果

    System.String: 10000000 iterations took 1329ms 
    System.Decimal: 10000000 iterations took 402ms 
    System.Double: 10000000 iterations took 38ms