时间:2022-5-7来源:本站原创作者:佚名

本文探讨使用C#StringBuilder的最佳实践,用于减少内存分配,提高字符串操作的性能。

在.NET中,字符串是不可变的类型。每当你在.NET中修改一个字符串对象时,就会在内存中创建一个新的字符串对象来保存新的数据。相比之下,StringBuilder对象代表了一个可变的字符串,并随着字符串大小的增长动态地扩展其内存分配。

String和StringBuilder类是你在.NETFramework和.NETCore中处理字符串时经常使用的两个流行类。然而,每个类都有其优点和缺点。

BenchmarkDotNet是一个轻量级的开源库,用于对.NET代码进行基准测试。BenchmarkDotNet可以将你的方法转化为基准,跟踪这些方法,然后提供对捕获的性能数据的洞察力。在这篇文章中,我们将利用BenchmarkDotNet为我们的StringBuilder操作进行基准测试。

要使用本文提供的代码示例,你的系统中应该安装有VisualStudio或者以上版本。

1.在VisualStudio中创建一个控制台应用程序项目

首先让我们在VisualStudio中创建一个.NETCore控制台应用程序项目。假设你的系统中已经安装了VisualStudio,请按照下面的步骤创建一个新的.NETCore控制台应用程序项目。

1.启动VisualStudioIDE。

2.点击"创建新项目"。

3.在"创建新项目"窗口中,从显示的模板列表中选择"控制台应用程序(.NET核心)"。

4.点击"下一步"。

5.在接下来显示的"配置你的新项目"窗口中,指定新项目的名称和位置。

6.点击创建。

这将在VisualStudio中创建一个新的.NETCore控制台应用程序项目。我们将在本文的后续章节中使用这个项目来处理StringBuilder。

2.安装BenchmarkDotNetNuGet包

要使用BenchmarkDotNet,你必须安装BenchmarkDotNet软件包。你可以通过VisualStudioIDE内的NuGet软件包管理器,或在NuGet软件包管理器控制台执行以下命令来完成。

1

Install-PackageBenchmarkDotNet

3.使用StringBuilderCache来减少分配

StringBuilderCache是一个内部类,在.NET和.NETCore中可用。每当你需要创建多个StringBuilder的实例时,你可以使用StringBuilderCache来大大减少分配的成本。

StringBuilderCache的工作原理是缓存一个StringBuilder实例,然后在需要一个新的StringBuilder实例时重新使用它。这减少了分配,因为你只需要在内存中拥有一个StringBuilder实例。

让我们用一些代码来说明这一点。在Program.cs文件中创建一个名为StringBuilderBenchmarkDemo的类。创建一个名为AppendStringUsingStringBuilder的方法,代码如下。

publicstringAppendStringUsingStringBuilder(){varstringBuilder=newStringBuilder();stringBuilder.Append("FirstString");stringBuilder.Append("SecondString");stringBuilder.Append("ThirdString");returnstringBuilder.ToString();}

上面的代码片段显示了如何使用StringBuilder对象来追加字符串。接下来创建一个名为AppendStringUsingStringBuilderCache的方法,代码如下。

publicstringAppendStringUsingStringBuilderCache(){varstringBuilder=StringBuilderCache.Acquire();stringBuilder.Append("FirstString");stringBuilder.Append("SecondString");stringBuilder.Append("ThirdString");returnStringBuilderCache.GetStringAndRelease(stringBuilder);}

上面的代码片段说明了如何使用StringBuilderCache类的Acquire方法创建一个StringBuilder实例,然后用它来追加字符串。

下面是StringBuilderBenchmarkDemo类的完整源代码供你参考。

[MemoryDiagnoser]publicclassStringBuilderBenchmarkDemo{[Benchmark]publicstringAppendStringUsingStringBuilder(){varstringBuilder=newStringBuilder();stringBuilder.Append("FirstString");stringBuilder.Append("SecondString");stringBuilder.Append("ThirdString");returnstringBuilder.ToString();}[Benchmark]publicstringAppendStringUsingStringBuilderCache(){varstringBuilder=StringBuilderCache.Acquire();stringBuilder.Append("FirstString");stringBuilder.Append("SecondString");stringBuilder.Append("ThirdString");returnStringBuilderCache.GetStringAndRelease(stringBuilder);}}

你现在必须使用BenchmarkRunner类来指定初始起点。这是一种通知BenchmarkDotNet在指定的类上运行基准的方式。

用以下代码替换Main方法的默认源代码。

staticvoidMain(string[]args){varsummary=BenchmarkRunner.RunStringBuilderBenchmarkDemo();}

现在在Release模式下编译你的项目,并在命令行使用以下命令运行基准测试。

dotnetrun-pStringBuilderPerfDemo.csproj-cRelease

?下面说明了两种方法的性能差异。

正如你所看到的,使用StringBuilderCache追加字符串要快得多,需要的分配也少。

4.使用StringBuilder.AppendJoin而不是String.Join

String对象是不可变的,所以修改一个String对象需要创建一个新的String对象。因此,在连接字符串时,你应该使用StringBuilder.AppendJoin方法,而不是String.Join,以减少分配,提高性能。

下面的代码列表说明了如何使用String.Join和StringBuilder.AppendJoin方法来组装一个长字符串。

[Benchmark]publicstringUsingStringJoin(){varlist=newListstring{"A","B","C","D","E"};varstringBuilder=newStringBuilder();for(inti=0;i;i++){stringBuilder.Append(string.Join(,list));}returnstringBuilder.ToString();}[Benchmark]publicstringUsingAppendJoin(){varlist=newListstring{"A","B","C","D","E"};varstringBuilder=newStringBuilder();for(inti=0;i;i++){stringBuilder.AppendJoin(,list);}returnstringBuilder.ToString();}

  

下图显示了这两种方法的基准测试结果。

请注意,对于这个操作,这两种方法的速度很接近,但StringBuilder.AppendJoin使用的内存明显较少。

5.使用StringBuilder追加单个字符

注意,在使用StringBuilder时,如果需要追加单个字符,应该使用Append(char)而不是Append(String)。

请考虑以下两个方法。

[Benchmark]publicstringAppendStringUsingString(){varstringBuilder=newStringBuilder();for(inti=0;i;i++){stringBuilder.Append("a");stringBuilder.Append("b");stringBuilder.Append("c");}returnstringBuilder.ToString();}[Benchmark]publicstringAppendStringUsingChar(){varstringBuilder=newStringBuilder();for(inti=0;i;i++){stringBuilder.Append(a);stringBuilder.Append(b);stringBuilder.Append(c);}returnstringBuilder.ToString();}

从名字中就可以看出,AppendStringUsingString方法说明了如何使用一个字符串作为Append方法的参数来追加字符串。

AppendStringUsingChar方法说明了你如何在Append方法中使用字符来追加字符。

下图显示了这两种方法的基准测试结果。

6.其他StringBuilder优化方法

StringBuilder允许你设置容量以提高性能。如果你知道你要创建的字符串的大小,你可以相应地设置初始容量以大大减少内存分配。

你还可以通过使用一个可重复使用的StringBuilder对象池来避免分配来提高StringBuilder的性能。

最后,请注意,由于StringBuilderCache是一个内部类,你需要将源代码粘贴到你的项目中才能使用它。回顾一下,在C#中你只能在同一个程序集或库中使用一个内部类。

因此,我们的程序文件不能仅仅通过引用StringBuilderCache所在的库来访问StringBuilderCache类。

这就是为什么我们把StringBuilderCache类的源代码复制到我们的程序文件中,也就是Program.cs文件。

出处:
转载请注明原文网址:http://www.helimiaopu.com/cxys/cxys/11926.html

------分隔线----------------------------