alexeyfv

Опубликовано

- 1 мин чтения

StringBuilder: избегайте ToString при использовании Append

C# Performance Code Quality
img of StringBuilder: избегайте ToString при использовании Append

Это новая часть серии, в которой я разбираю правила качества кода .NET с точки зрения производительности. Сегодня поговорим про CA1830: Prefer strongly-typed Append and Insert method overloads on StringBuilder.

Описание правила

Правило рекомендует не преобразовывать значения в строку при вызове методов Append или Insert.

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

Вместо этого лучше передать значение напрямую:

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

Это правило касается примитивных типов вроде byte, short, int, double, long и других.

Анализ производительности

Я написал простой бенчмарк, чтобы оценить, насколько эти подходы отличаются по производительности. Результаты в репозитории и на диаграмме ниже.

График сравнения производительности

График сравнения производительности

Разница по времени — небольшая: около 5 микросекунд на моём ноутбуке. Разница по памяти более заметная: около 20 КиБ.

Как работает эта оптимизация?

Когда вы используете метод Append(int), под капотом StringBuilder вызываются методы AppendSpanFormattable<T> и InsertSpanFormattable<T>.

Например, упрощённая версия 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());  
}

Этот метод использует интерфейс ISpanFormattable.TryFormat, чтобы записать значение напрямую во внутренний буфер StringBuilder как Span<char>. Это позволяет: избежать аллокации строки; упаковки (boxing) и вызова виртуального метода ToString().

Стоит ли заморачиваться?

Скорее да. Даже если не брать в расчёт прирост производительности, такой код:

   sb.Append(i);

и выглядит проще и чище, чем:

   sb.Append(i.ToString());

И в качестве бонуса, он работает быстрее и потребляет меньше памяти. Win-win.