C# 11 string interpolation
New features of C# 11 in .NET 6 results in super-optimized string interpolation.
Sample
string FormatVersion(int major, int minor, int build, int revision) =>
$"{major}.{minor}.{build}.{revision}";
Before C# 11
Approximation of what C# 10 would generate:
string FormatVersion(int major, int minor, int build, int revision)
{
var array = new object[4];
array[0] = major;
array[1] = minor;
array[2] = build;
array[3] = revision;
return string.Format("{0}.{1}.{2}.{3}", array);
}
Since C# 11
Approximation of what C# 10 would generate:
string FormatVersion(int major, int minor, int build, int revision)
{
var handler = new DefaultInterpolatedStringHandler(literalLength: 3, formattedCount: 4);
handler.AppendFormatted(major);
handler.AppendLiteral(".");
handler.AppendFormatted(minor);
handler.AppendLiteral(".");
handler.AppendFormatted(build);
handler.AppendLiteral(".");
handler.AppendFormatted(revision);
return handler.ToStringAndClear();
}
Benchmark
C# | .NET | Mean | Ratio | Allocated |
---|---|---|---|---|
10 | 5 | 111.70 ns | 1.00 | 192 B |
11 | 6 | 66.75 ns | 0.60 | 40 B |
Note: The results are highly subjective to the machine running the benchmark.
Key improvements
No need for
object[4]
allocation.No need for the 4
int
boxing allocations.Now supports writing
ref struct
types, such asReadOnlySpan<char>
Now supports writing into an existing
Span<char>
via the new extension methodMemoryExtensions.TryWrite
, eg:Span<char> mySpan = stackalloc char[64]; var ok = mySpan.TryWrite(null, $"{major}.{minor}.{build}.{revision}", out var charsWritten);
String interpolation, including when using aforementioned
MemoryExtensions.TryWrite
, can take use of newlypublic
-madeISpanFormattable
as an allocation-freeToString
alternative.StringBuilder.Append
and.AppendLine
also supports these new string interpolations viaISpanFormattable
, meaning the following.Append
is also fast and allocation-free:var builder = new StringBuilder(); // obviously not allocation-free builder.Append($"{major}.{minor}.{build}.{revision}"); // allocation-free!
Under the hood
Uses
ArrayPool<char>.Shared
for allocation-less string building.Can be overridden to use
stackalloc
’d spans instead:string FormatVersion(int major, int minor, int build, int revision) => string.Create(null, stackalloc char[64], $"{major}.{minor}.{build}.{revision}");
DefaultInterpolatedStringHandler
grows if theISpanFormattable.TryFormat
returns false, and then repeats until.TryFormat
returns true. The following works, but reverts to usingArrayPool<char>.Shared
:string FormatVersion(int major, int minor, int build, int revision) => string.Create(null, stackalloc char[5], $"{major}.{minor}.{build}.{revision}");