Tiny, fast ref struct string builder.
Backed by ArrayPool<char>. Low allocations. Short-lived use.
dotnet add package Soenneker.Utils.PooledStringBuildersusing Soenneker.Utils.PooledStringBuilders;
using var sb = new PooledStringBuilder(128);
sb.Append("Hello, ");
sb.Append(name);
sb.Append(' ');
sb.Append(id); // ISpanFormattable path, no boxing
sb.AppendLine();
string s = sb.ToString(); // creates a string; using returns the buffer afterwardUse ToString() when the builder remains in scope and will be disposed separately. For a one-shot
finish without a using declaration:
var sb = new PooledStringBuilder();
sb.Append("value=");
sb.Append(value);
string result = sb.ToStringAndDispose();new PooledStringBuilder(int capacity = 128)Append(char),Append(string?),Append(ReadOnlySpan<char>)Append<T>(T value, ReadOnlySpan<char> format = default, IFormatProvider? provider = null)whereT : ISpanFormattableAppendSpan(int length)— reserve and write directly into the bufferInsert(...),Shrink(int),AppendLine(...),AppendSeparatorIfNotEmpty(char)Length,Capacity,AsSpan(),EnsureCapacity(int),Clear()ToString()— create a string without disposing the builderToStringAndDispose(bool clear = false)— create a string and return the bufferDispose()/Dispose(bool clear)
PooledStringBuilderis a stack-onlyref struct; it cannot be boxed, captured, stored in a normal field, or kept acrossawait.- Do not copy the builder (
var copy = builder). Copies refer to the same rented array and can return it to the pool more than once. Pass it byrefwhen a helper must mutate the same builder. - Dispose exactly once, either through
using,Dispose, orToStringAndDispose. Do not useToStringAndDisposeand then dispose a copied or aliased value. AppendSpan(length)immediately increasesLengthand returns uninitialized pooled storage. Fill the entire span before reading or converting the builder, or previous pool contents could appear in the result.AsSpan()is valid only until the builder grows, changes, or is disposed. Do not retain it.AppendLineappends\n, notEnvironment.NewLine.Clear()resets the logical length but does not zero the array. UseDispose(clear: true)orToStringAndDispose(clear: true)when the pooled buffer contained secrets. The returned managed string is still immutable and cannot be securely erased.- The builder is not thread-safe. Keep it short-lived and confined to one synchronous scope.
