C# 字符串拼接的幾種方式及其性能分析對比
在C#編程中字符串拼接是一種常見且基礎的操作,廣泛應用于各種場景,如動態生成SQL查詢、構建日志信息、格式化用戶顯示內容等。然而,不同的字符串拼接方式在性能和內存使用上可能存在顯著差異。今天咱們一起來看看在C#中字符串拼接的常見6種方式及其使用BenchmarkDotNet進行性能分析對比。
BenchmarkDotNet
BenchmarkDotNet是一個基于.NET開源、功能全面、易于使用的性能基準測試框架,它為.NET開發者提供了強大的性能評估和優化能力。通過自動化測試、多平臺支持、高級統計分析和自定義配置等特性,BenchmarkDotNet幫助開發者更好地理解和優化軟件系統的性能表現。
拼接基礎數據
private const int IterationCount = 1000;
private const string StringPart1 = "追逐時光者";
private const string StringPart2 = "DotNetGuide";
private const string StringPart3 = "DotNetGuide技術社區";
private readonly string[] _stringPartsArray = { "追逐時光者", "DotNetGuide", "DotNetGuide技術社區" };
+操作符
/// <summary>
/// 使用 + 操作符拼接字符串
/// </summary>
/// <returns></returns>
[Benchmark]
public string PlusOperator()
{
string result = string.Empty;
for (int i = 0; i < IterationCount; i++)
{
result += StringPart1 + " " + StringPart2 + " " + StringPart3;
}
return result;
}
$內插字符串
/// <summary>
/// 使用 $ 內插字符串拼接字符串
/// </summary>
/// <returns></returns>
[Benchmark]
public string InterpolatedString()
{
string result = string.Empty;
for (int i = 0; i < IterationCount; i++)
{
result += $"{StringPart1} {StringPart2} {StringPart3}";
}
return result;
}
String.Format
/// <summary>
/// 使用string.Format()拼接字符串
/// </summary>
/// <returns></returns>
[Benchmark]
public string StringFormat()
{
string result = string.Empty;
for (int i = 0; i < IterationCount; i++)
{
result += string.Format("{0} {1} {2}", StringPart1, StringPart2, StringPart3);
}
return result;
}
String.Concat
/// <summary>
/// 使用string.Concat()拼接字符串
/// </summary>
/// <returns></returns>
[Benchmark]
public string StringConcat()
{
string result = string.Empty;
for (int i = 0; i < IterationCount; i++)
{
result += string.Concat(StringPart1, " ", StringPart2, " ", StringPart3);
}
return result;
}
String.Join
/// <summary>
/// 使用string.Join()拼接字符串
/// </summary>
/// <returns></returns>
[Benchmark]
public string StringJoin()
{
string result = string.Empty;
for (int i = 0; i < IterationCount; i++)
{
result += string.Join(" ", _stringPartsArray);
}
return result;
}
StringBuilder
/// <summary>
/// 使用StringBuilder拼接字符串
/// </summary>
/// <returns></returns>
[Benchmark]
public string StringBuilder()
{
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < IterationCount; i++)
{
stringBuilder.Append(StringPart1);
stringBuilder.Append(" ");
stringBuilder.Append(StringPart2);
stringBuilder.Append(" ");
stringBuilder.Append(StringPart3);
}
return stringBuilder.ToString();
}
性能基準對比測試完整代碼
[MemoryDiagnoser]//記錄內存分配情況
public class StringConcatenationBenchmark
{
private const int IterationCount = 1000;
private const string StringPart1 = "追逐時光者";
private const string StringPart2 = "DotNetGuide";
private const string StringPart3 = "DotNetGuide技術社區";
private readonly string[] _stringPartsArray = { "追逐時光者", "DotNetGuide", "DotNetGuide技術社區" };
/// <summary>
/// 使用 + 操作符拼接字符串
/// </summary>
/// <returns></returns>
[Benchmark]
public string PlusOperator()
{
string result = string.Empty;
for (int i = 0; i < IterationCount; i++)
{
result += StringPart1 + " " + StringPart2 + " " + StringPart3;
}
return result;
}
/// <summary>
/// 使用 $ 內插字符串拼接字符串
/// </summary>
/// <returns></returns>
[Benchmark]
public string InterpolatedString()
{
string result = string.Empty;
for (int i = 0; i < IterationCount; i++)
{
result += $"{StringPart1} {StringPart2} {StringPart3}";
}
return result;
}
/// <summary>
/// 使用string.Format()拼接字符串
/// </summary>
/// <returns></returns>
[Benchmark]
public string StringFormat()
{
string result = string.Empty;
for (int i = 0; i < IterationCount; i++)
{
result += string.Format("{0} {1} {2}", StringPart1, StringPart2, StringPart3);
}
return result;
}
/// <summary>
/// 使用string.Concat()拼接字符串
/// </summary>
/// <returns></returns>
[Benchmark]
public string StringConcat()
{
string result = string.Empty;
for (int i = 0; i < IterationCount; i++)
{
result += string.Concat(StringPart1, " ", StringPart2, " ", StringPart3);
}
return result;
}
/// <summary>
/// 使用string.Join()拼接字符串
/// </summary>
/// <returns></returns>
[Benchmark]
public string StringJoin()
{
string result = string.Empty;
for (int i = 0; i < IterationCount; i++)
{
result += string.Join(" ", _stringPartsArray);
}
return result;
}
/// <summary>
/// 使用StringBuilder拼接字符串
/// </summary>
/// <returns></returns>
[Benchmark]
public string StringBuilder()
{
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < IterationCount; i++)
{
stringBuilder.Append(StringPart1);
stringBuilder.Append(" ");
stringBuilder.Append(StringPart2);
stringBuilder.Append(" ");
stringBuilder.Append(StringPart3);
}
return stringBuilder.ToString();
}
}
性能基準對比測試分析報告
Method | Mean | Error | StdDev | Gen0 | Gen1 | Allocated |
PlusOperator | 2,066.28 us | 35.761 us | 63.566 us | 5238.2813 | 789.0625 | 32283.12 KB |
InterpolatedString | 1,984.56 us | 29.949 us | 28.014 us | 5238.2813 | 789.0625 | 32283.12 KB |
StringFormat | 2,112.02 us | 25.020 us | 23.404 us | 5257.8125 | 777.3438 | 32369.06 KB |
StringConcat | 2,027.09 us | 28.300 us | 26.472 us | 5257.8125 | 777.3438 | 32369.06 KB |
StringJoin | 2,017.36 us | 27.111 us | 22.639 us | 5257.8125 | 777.3438 | 32369.06 KB |
StringBuilder | 13.63 us | 0.065 us | 0.058 us | 23.2544 | 4.6387 | 143.96 KB |
說明:
- Mean: 所有測量值的算術平均值。
- Error: 99.9% 置信區間的一半。
- StdDev: 所有測量值的標準差。
- Gen0: 第 0 代 GC 每 1000 次操作收集一次。
- Gen1: 第 1 代 GC 每 1000 次操作收集一次。
- Gen2: 第 2 代 GC 每 1000 次操作收集一次。
- Allocated: 每次操作分配的內存(僅托管內存,包含所有內容,1KB = 1024B)。
- 1 ms: 1 毫秒(0.001 秒)。
性能基準對比測試結論
從上面的性能基準對比測試分析報告來看StringBuilder是性能最好的字符串拼接方式,特別是在需要頻繁進行拼接的場景中。其他方式(如+操作符、$內插字符串、String.Format、String.Concat和String.Join)在性能上相對較差,因為它們會導致多次內存分配和復制。
因此我們在選擇字符串拼接方式時,應該根據具體場景和需求進行選擇。如果性能是關鍵因素,并且需要頻繁進行拼接,則應使用StringBuilder。如果代碼簡潔性和易讀性更重要,并且拼接次數較少,則可以考慮使用其他方式。