Comparing String Concatenation Methods in C#: Benchmarks and Best Practices

Sheldon Cohen
5 min readAug 1, 2024

--

Best Practices for String Manipulation in C#

String manipulation is a common task in C#, and knowing the best practices for concatenation and interpolation can greatly affect the readability and performance of your code. Let’s explore various techniques and discuss their pros and cons, including performance considerations.

Using the + Operator

The + operator is the simplest way to concatenate strings. For example, if you have a firstName and lastName, you can concatenate them as follows:

string firstName = "John";
string lastName = "Doe";
string fullName = firstName + " " + lastName;

This method is easy to read and understand, making it ideal for simple concatenations. However, it becomes inefficient when dealing with large numbers of concatenations due to the creation of multiple intermediate strings, making it unsuitable for performance-critical applications.

Using String Concat

string.Concat is a static method that can concatenate multiple strings. Here’s an example:

string fullName = string.Concat(firstName, " ", lastName);

This method is more efficient than the + operator for multiple strings because it does not create intermediate strings. However, it is slightly less readable for simple cases.

Using StringBuilder

For efficient string manipulation, especially with large or numerous concatenations, StringBuilder is a preferred choice:

using System.Text;
StringBuilder sb = new StringBuilder();
sb.Append(firstName);
sb.Append(" ");
sb.Append(lastName);
string fullName = sb.ToString();

StringBuilder is highly efficient for large or multiple concatenations and reduces memory overhead by minimizing intermediate strings. Although it is slightly more verbose than other methods and requires initializing a StringBuilder instance, it is very efficient for repeated operations.

Using String Interpolation

String interpolation offers a readable and concise way to format strings:

string fullName = $"{firstName} {lastName}";

This method is highly readable and supports complex formatting directly within the string. However, internally it uses string.Format, which may be less efficient than StringBuilder for extensive manipulations.

Using String Format

string.Format provides a way to concatenate strings using placeholders:

string fullName = string.Format("{0} {1}", firstName, lastName);

This method is useful for formatting strings with multiple data types and is readable for those familiar with placeholders. However, it is less concise than string interpolation and can be less efficient than StringBuilder for extensive operations.

Performance Benchmarks

To compare the performance of these methods, we use BenchmarkDotNet. This benchmark setup is configured to ensure sufficient iterations and warm-up times for accurate measurement:

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System.Text;

[MemoryDiagnoser]
public class StringConcatenationBenchmarks
{
private string firstName = "John";
private string lastName = "Doe";

[Benchmark]
public string PlusOperator() => firstName + " " + lastName;

[Benchmark]
public string StringConcat() => string.Concat(firstName, " ", lastName);

[Benchmark]
public string StringBuilder()
{
var sb = new StringBuilder();
sb.Append(firstName);
sb.Append(" ");
sb.Append(lastName);
return sb.ToString();
}

[Benchmark]
public string StringInterpolation() => $"{firstName} {lastName}";

[Benchmark]
public string StringFormat() => string.Format("{0} {1}", firstName, lastName);
}

class Program
{
static void Main(string[] args)
{
var summary = BenchmarkRunner.Run<StringConcatenationBenchmarks>();
}
}

Benchmark Results

The benchmark results, run with sufficient iterations, are as follows:

|                Method |       Mean |    Error |   StdDev  |     Median |     Gen 0 |  Allocated |
|---------------------- |-----------:|---------:|----------:|-----------:|----------:|-----------:|
| PlusOperator | 22.70 ns | 1.129 ns | 3.257 ns | 21.96 ns | 0.0063 | 40 B |
| StringConcat | 23.77 ns | 1.757 ns | 4.956 ns | 21.66 ns | 0.0063 | 40 B |
| StringBuilder | 43.52 ns | 1.230 ns | 3.428 ns | 42.75 ns | 0.0229 | 144 B |
| StringInterpolation | 38.30 ns | 5.526 ns | 16.293 ns | 29.93 ns | 0.0063 | 40 B |
| StringFormat | 105.35 ns | 8.278 ns | 23.616 ns | 93.92 ns | 0.0063 | 40 B |

If you have two or three strings, use string concatenation. StringBuilder will improve performance in cases where you make repeated modifications to a string or concatenate many strings together. StringBuilder is best suited for a large number of concatenations.

Analysis of Benchmark Results

  1. Plus Operator:
  • Mean: 22.70 ns
  • Error: 1.129 ns
  • StdDev: 3.257 ns
  • Median: 21.96 ns
  • Gen 0: 0.0063
  • Allocated: 40 B

The + operator is relatively fast and allocates minimal memory. It is suitable for simple concatenations but may create multiple intermediate strings.

2. String.Concat:

  • Mean: 23.77 ns
  • Error: 1.757 ns
  • StdDev: 4.956 ns
  • Median: 21.66 ns
  • Gen 0: 0.0063
  • Allocated: 40 B

string.Concat has similar performance to the + operator but avoids intermediate string creation, making it slightly more efficient in some cases.

3. StringBuilder:

  • Mean: 43.52 ns
  • Error: 1.230 ns
  • StdDev: 3.428 ns
  • Median: 42.75 ns
  • Gen 0: 0.0229
  • Allocated: 144 B

StringBuilder is slower for single concatenations but excels in scenarios with multiple or large concatenations, as it reduces memory overhead significantly.

4. String Interpolation:

  • Mean: 38.30 ns
  • Error: 5.526 ns
  • StdDev: 16.293 ns
  • Median: 29.93 ns
  • Gen 0: 0.0063
  • Allocated: 40 B

String interpolation is a good balance between readability and performance. It is slightly slower than the + operator and string.Concat, but it is more readable and supports complex formatting.

5. String.Format:

  • Mean: 105.35 ns
  • Error: 8.278 ns
  • StdDev: 23.616 ns
  • Median: 93.92 ns
  • Gen 0: 0.0063
  • Allocated: 40 B

string.Format is the slowest among the methods tested. It offers flexibility and readability but should be used sparingly in performance-critical code.

Memory and Garbage Collection

The benchmark results include memory allocation and garbage collection metrics:

  • Plus Operator: Allocates 40 bytes of memory and generates 0.0063 Gen 0 garbage collections per operation.
  • String.Concat: Allocates 40 bytes of memory and generates 0.0063 Gen 0 garbage collections per operation.
  • StringBuilder: Allocates 144 bytes of memory and generates 0.0229 Gen 0 garbage collections per operation.
  • String Interpolation: Allocates 40 bytes of memory and generates 0.0063 Gen 0 garbage collections per operation.
  • String.Format: Allocates 40 bytes of memory and generates 0.0063 Gen 0 garbage collections per operation.

Wrapping it up

Choosing the right string manipulation method in C# depends on the specific needs of your application. For simple and readable code, the + operator or string interpolation is often sufficient. For performance-critical applications, especially those involving numerous or large concatenations, StringBuilder is the optimal choice. Understanding these methods and their performance implications, including memory usage and garbage collection, will help you write more efficient and maintainable C# code. Additionally, when logging, string interpolation provides a clean and efficient way to include variable data in log messages.

Share your experiences in the comments. Follow me for more articles on Software Engineering, .NET and Software Development best practices.

--

--

Sheldon Cohen

Technology professional with 15+ years of software development