alexeyfv

Published on

- 1 min read

StringBuilder tip: use Append without ToString

C# Performance Code Quality
img of StringBuilder tip: use Append without ToString

This is a new part of a series where I analyze .NET code quality rules from a performance perspective. Today, let’s talk about CA1830: Prefer strongly-typed Append and Insert method overloads on StringBuilder.

Rule description

This rule suggest us not to convert parameters to string when using Append or Insert methods:

   var i = 123;  
var sb = new StringBuilder();  
sb.Append(i.ToString()); // CA1830

Instead, pass the value directly:

   var i = 123;  
var sb = new StringBuilder();  
sb.Append(i);

This rule applies to well-known primitive types like byte, short, int, double, long, and so on.

Performance analysis

I wrote a simple benchmark to see how much these approaches really affect performance. The results are in the repo and on the diagram below.

Performance comparison chart

Performance comparison chart

There’s a small time difference between these two approaches — about 5 microseconds on my laptop. The memory difference is more significant — about 20 KiB.

How the optimization works?

When you use a strongly-typed overload like Append(int), .NET internally calls methods like: AppendSpanFormattable<T> and InsertSpanFormattable<T>.

Here’s a simplified version of AppendSpanFormattable<T>:

   private StringBuilder AppendSpanFormattable<T>(T value) where T : ISpanFormattable  
{  
    if (value.TryFormat(RemainingCurrentChunk, out int charsWritten, format: default, provider: null))  
    {  
        m_ChunkLength += charsWritten;  
        return this;  
    }

    return Append(value.ToString());  
}

This method uses ISpanFormattable.TryFormat to write the value directly into the internal buffer as a Span<char>. This avoids: allocating a temporary string, boxing value types, calling the virtual ToString() method.

Should you care?

Yes, you probably should. Even if we don’t care about performance benefits, this code:

   sb.Append(i);

…is cleaner and simpler than:

   sb.Append(i.ToString());

And as a bonus — it’s faster and allocates less memory. Win-win.