При работе со StringBuilder есть ещё одна неприятность: метод StringBuilder.Append прекрасен, но, по какой-то странной для меня причине, не имеет перегрузки для DateTime. Я знаю, что многие разработчики просто передают туда дату и... попадают на boxing, поскольку будет выбран метод с сигнатурой, принимающей object. Либо, что тоже странно, коллеги просто делают dateTime.ToString(), аллоцируя промежуточную строку и просто передавая её в StringBuilder.Append.
Вроде бы ничего страшного, вроде бы очень очень маленькая аллокация. Но на больших объемах GC будет чаще собирать мусор, чаще нагружать процессор, а значит будет оставаться меньше процессорного времени для бизнесовых задач. Не надо так.
Победить это просто. Мы уже знаем про метод AppendSpanFormattable из реализации ValueStringBuilder, который принимает значения, реализующие ISpanFormattable. Следовательно, нам нужно просто создать метод-расширение для StringBuilder'a с почти таким же кодом:
public static StringBuilder AppendSpanFormattable<T>(this StringBuilder sb, T value, string format, int bufferSize = 64) where T: ISpanFormattable {
Span<char> buffer = stackalloc char[bufferSize];
if (value.TryFormat(buffer, out var written, format, null)) {
sb.Append(buffer[..written]);
} else {
sb.Append(value);
}
return sb;
}
Результаты улучшения на скриншоте. Обратите внимание, что аллокация пропала и результат очень похож на результаты с ValueStringBuilder.
Код бенчмарка в комментариях.
P.S.: Если вы сразу хотите перейти к правильному варианту, минуя ValueStringBuilder'ы и прочие ухищрения - см. вот этот коммент. Другой вопрос, что без понимания КАК это работает, будет сложно это заиспользовать - выглядит как натуральная магия.
Мой канал в TG: https://t.me/csharp_gepard