Efficient String Concatenation in C# Using Span<string>
In modern .NET development, especially with high-performance and memory-sensitive applications, efficient string handling is crucial. C# strings are immutable — every concatenation creates a new string object. This can quickly lead to excessive allocations when you build large strings dynamically (for example, in parsers, log builders, or network protocols).
Traditionally, developers have turned to StringBuilder to mitigate this. However, the introduction of Span<T> and ReadOnlySpan<T> in newer versions of .NET provides an alternative that is even more lightweight and allocation-friendly.
Let’s explore how Span<string> (and its read-only counterpart) can be leveraged for efficient string concatenation.
Understanding the Problem
When you write code like this:
var result = a + b + c + d;
C# creates multiple intermediate string objects — one for each + operation. Even in a loop, this can result in a large number of temporary strings, causing both CPU and GC pressure.
For example:
string result = "";
for (int i = 0; i < 1000; i++)
{
result += i.ToString();
}
This allocates 1000 intermediate strings, which is highly inefficient.
Enter Span<string>
A Span<T> is a stack-only type that represents a contiguous region of arbitrary memory. When used with strings, it lets you manipulate slices of string arrays (or collections of strings) without creating new arrays or copying data.
You can use a Span<string> to aggregate multiple strings into a buffer, then combine them efficiently at the end with string.Concat(ReadOnlySpan<string>).
Using string.Concat(ReadOnlySpan<string>)
Since .NET Core 3.0, there’s an overload of string.Concat that accepts a ReadOnlySpan<string>. This is ideal for cases where you already have your strings in a stack-allocated span or an array.
Example:
using System;
public static class SpanConcatExample
{
public static void Main()
{
Span<string> words = stackalloc string[4];
words[0] = "Eagle";
words[1] = "Swift";
words[2] = "Consulting";
words[3] = "Rocks!";
string result = string.Concat(words);
Console.WriteLine(result); // Output: EagleSwiftConsultingRocks!
}
}
Why this is powerful:
Stack allocation:
stackalloccreates the array on the stack, avoiding heap allocations.Zero temporary strings:
string.Concat(ReadOnlySpan<string>)computes the final string length, then allocates only once for the final result.Predictable performance: The entire operation involves one allocation and a single pass to copy all content.
A More Practical Example
Suppose you’re dynamically composing a log line from several parts:
using System;
public static class Logger
{
public static string BuildLogLine(DateTime timestamp, string level, string message)
{
Span<string> parts = stackalloc string[5];
parts[0] = timestamp.ToString("O");
parts[1] = " [";
parts[2] = level;
parts[3] = "] ";
parts[4] = message;
return string.Concat(parts);
}
public static void Main()
{
string log = BuildLogLine(DateTime.UtcNow, "INFO", "Application started");
Console.WriteLine(log);
}
}
Result:
2025-11-05T16:52:00.1234567Z [INFO] Application started
This technique scales beautifully when you’re concatenating known, small numbers of strings repeatedly in high-frequency code (like request handling or logging).
Performance Notes
MethodAllocationsPerformance (relative)Notesa + b + cHighSlowCreates intermediate stringsStringBuilderLowMedium-HighGood for unknown or looping concatenationstring.Concat(params string[])MediumHighAllocates array on heapstring.Concat(ReadOnlySpan<string>)Very LowFastestSingle allocation; stack-friendly
In microbenchmarks, string.Concat(ReadOnlySpan<string>) often matches or exceeds StringBuilder performance when concatenating a fixed, small set of strings, with fewer allocations and simpler syntax.
When to Use This
✅ Use Span<string> or ReadOnlySpan<string> with string.Concat() when:
You are concatenating a known number of strings (e.g., 3-10 parts).
You want stack-based and allocation-free temporary storage.
You’re working in high-performance or low-allocation code paths.
🚫 Avoid it when:
You need to concatenate a variable or very large number of strings —
StringBuilderorValueStringBuildermay still be better.You’re writing code that must work in unsafe or unmanaged contexts (since
stackallochas stack limits).
Final Thoughts
Using Span<string> for string concatenation is a small but powerful optimization in modern C#. It combines clarity, performance, and memory efficiency, all while leveraging the newer string.Concat(ReadOnlySpan<string>) API.
If you’re building high-throughput systems (web APIs, game engines, or telemetry services), these micro-optimizations add up. Next time you’re assembling strings from multiple parts, consider using Span<string> — your GC will thank you.
Tags: #CSharp #Performance #Span #StringConcat #DotNet