你确认C#中TryParse比Parse好?动手测试才知道!

继上一篇“你知道C#中的强制类型转换与 as 和 is吗?”后,本文继续讲《编写高质量代码改善C#程序的157个建议》一书第4个建议“TryParse比Parse好”。喜欢本书请到各大商城购买原书,支持正版。

如果注意观察除string 外的所有基元类型,会发现它们都有两个将字符串转型为本身的方法:Parse和TryParse。以类型double为例,这两个方法最简单的原型为:

public static double Parse(string s)
public static bool TryParse(string s,out double result)

两者最大的区别是,如果字符串格式不满足转换的要求,Parse方法将会引发一个异常;TryParse方法则不会引发异常,它会返回false,同时将result置为0。

实际上,早期的FCL中并没有提供TryParse方法,那时只能调用Parse方法,如果转型失败,则要将值设定为一个初始值,同时必须要捕获异常,代码如下所示:

string str = nul1;
double d;
try
{
  a = Double.Parse(str);
)
catch (Exception ex)
{
  d = 0;
)

要注意,引发异常这个过程会对性能造成损耗(第5章会详细解释这一点)。微软的开发团队正是注意到这一点,所以从.NET 2.0开始,FCL中开始为基元类型提供TryParse方法。我们不妨来做个实验,代码如下所示(控制台程序):

double re;
long ticks;

Stopwatch sw = Stopwatch.StartNew();
for (int i = 1; i < 1000; i++)
{
  try
  {
    re = double.Parse("123");
  }
  catch
  {
    re = 0;
  }
}
sw.Stop();
ticks = sw.ElapsedTicks;
Console.WriteLine($"double.Parse()成功,{ticks} ticks");

sw = Stopwatch.StartNew();
for (int i = 1; i < 1000; i++)
{
  if (double.TryParse("123", out re) == false)
  {
    re = 0;
  }
}
sw.Stop();
ticks = sw.ElapsedTicks;
Console.WriteLine($"double.TryParse()成功,{ticks} ticks");

sw = Stopwatch.StartNew();
for (int i = 1; i < 1000; i++)
{
  try
  {
    re = double.Parse("aaa");
  }
  catch
  {
    re = 0;
  }
}
sw.Stop();
ticks = sw.ElapsedTicks;
Console.WriteLine($"double.Parse()失败,{ticks} ticks");

sw = Stopwatch.StartNew();
for (int i = 1; i < 1000; i++)
{
  if (double.TryParse("aaa", out re) == false)
  {
    re = 0;
  }
}
sw.Stop();
ticks = sw.ElapsedTicks;
Console.WriteLine("double.TryParse()失败,{0} ticks", ticks);

以上这段代码的输出为:

double.Parse()成功,6661 ticks
double.TryParse()成功,2886 ticks
double.Parse()失败,2062347 ticks
double.TryParse()失败,3379 ticks

可见,Parse和TryParse方法如果执行成功,它们的效率在一个数量级上,甚至在本示例中(在一个循环内),TryParse所带来的效率比Parse还要高一些。但若执行失败,Parse的执行效率相比于TryParse就太低了。【注1:本段与站长实际测试有出入,见文末

我们将提供TryParse方法的这种行为叫做类型提供TryParse模式。TryParse模式为类型提供两个方法,假设第一个方法声明为Do,第二个方法则声明为TryDo。Do方法在执行过程中如果发生错误则引发异常,而TryDo方法会返回一个boolean 值,方法执行失败返回false。如果要从TryDo中获取实际的返回值,则应该为方法提供out参数。

不过,我们并不建议为所有的类型都提供TryParse模式,只有在考虑到Do方法会带来明显的性能损耗时,才建议使用TryParse。

站长注: 上面相同的代码,站长测试输出结果与原书输出结果出入较多,以下是尝试两次运行输出结果,与注1一段描述有1点不同:Parse和TryParse方法如果执行成功,它们的效率在一个数量级上,TryParse所带来的效率比Parse要高很多;

第一次:

double.Parse()成功,206250 ticks
double.TryParse()成功,683 ticks
double.Parse()失败,110957205 ticks
double.TryParse()失败,571 ticks

第二次:

double.Parse()成功,267469 ticks
double.TryParse()成功,921 ticks
double.Parse()失败,110679229 ticks
double.TryParse()失败,574 ticks

下一篇我们接着读《建议5:使用int?来确保值类型也可以为null》,欢迎关注微信公众号【小市民在西河】。

原文出处:微信公众号【小市民在西河】

原文链接:

本文观点不代表Dotnet9立场,转载请联系原作者。

发表评论

登录后才能评论

评论列表(5条)

  • 君一席
    君一席 2021年2月25日 08:58

    直接复制代码跑了一下。
    我这里的结果和文章作者的比较一致,相同的量级与差距。没能达到和站长那样的测试效果水平……

    不知是否与电脑的硬件有关,这里4G CPU+16G 内存 VS2019

    要是评论支持贴图就好了

    • Dotnet9
      Dotnet9 2021年2月25日 11:01

      @君一席文中测试我使用的.net 6.0 preview1,你也把数据贴一下呢,我马上在公司这笔记本试试:
      .net 6.0 preview1控制台输出如下:
      double.Parse()成功,63186 ticks
      double.TryParse()成功,766 ticks
      double.Parse()失败,488050 ticks
      double.TryParse()失败,612 ticks

      .net framework 4.5控制台输入如下:
      double.Parse()成功,3650 ticks
      double.TryParse()成功,1499 ticks
      double.Parse()失败,601585 ticks
      double.TryParse()失败,2135 ticks

  • 君一席
    君一席 2021年2月25日 13:10

    .net 6.0 preview1需要VS2019 16.8及以上版本,目前不具备测试环境。

    只跑了.net framework 4.5,如下为其中一组数据:
    double.Parse()成功,4940 ticks
    double.TryParse()成功,916 ticks
    double.Parse()失败,393493 ticks
    double.TryParse()失败,808 ticks

    • Dotnet9
      Dotnet9 2021年2月25日 14:16

      @君一席比较一下,.net framework的parse比.net 的parse快,tryparse都差不多,我用.net core 3.1测试数据:
      double.Parse()成功,19266 ticks
      double.TryParse()成功,928 ticks
      double.Parse()失败,358220 ticks
      double.TryParse()失败,958 ticks

      不纠结这个,用tryparse就是了,哈哈。

    • hambor
      hambor 2021年2月28日 11:37

      @Dotnet9好,支持