Перегрузки StringBuilder.Append и порядок вычисления
В C# 10 добавлена поддержка более эффективной интерполяции строк, включая возможность назначения настраиваемых обработчиков в дополнение к строкам. StringBuilder используются преимущества новых перегрузок Append и AppendLine, которые принимают настраиваемый обработчик строк с интерполяцией. Существующие вызовы этих методов теперь могут начать привязку к новым перегрузкам. Как правило, поведение такое же, только с улучшенной производительностью. Например, вместо первоначального создания строки и последующего добавления этой строки отдельные компоненты интерполяции строки добавляются непосредственно в построитель. Однако это может изменить порядок вычисления объектов, используемых в качестве элементов форматирования, что может считаться отличием в поведении.
Прежнее поведение
В предыдущих версиях вызов выполняется следующим образом:
stringBuilder.Append($"{a} {b}");
компилируется в эквивалент следующего:
stringBuilder.Append(string.Format("{0} {1}", a, b));
Это означает, что вычисляется a
, затем оценивается b
, затем на основе результатов этих оценок создается строка, которая затем добавляется к построителю.
Новое поведение
Начиная с .NET 6, вызов:
stringBuilder.Append($"{a} {b}");
компилируется в эквивалент следующего:
var handler = new StringBuilder.AppendInterpolatedStringHandler(1, 2, stringBuilder);
handler.AppendFormatted(a);
handler.AppendLiteral(" ");
handler.AppendFormatted(b);
stringBuilder.Append(ref handler);
Это означает, что вычисляется a
и добавляется в построитель, а затем вычисляется b
и добавляется в построитель.
Если, например, a
или b
является самим построителем, как показано в следующем коде, новый порядок вычисления может привести к различному поведению во время выполнения.
stringBuilder.Append($"{a} {stringBuilder}");
Представленные версии
6.0 RC 1
Тип критического изменения
Это изменение может повлиять на совместимость исходного кода.
Причина изменения
Разработчики часто передают интерполированные строки в StringBuilder, поскольку это более удобно, чем разделение строки вручную и вызов StringBuilder.Append для каждой части. Эти новые перегрузки позволяют использовать краткий синтаксис и большую эффективность отдельных вызовов.
Рекомендуемое действие
В большинстве случаев, когда используются StringBuilder.Append и StringBuilder.AppendLine, разница в функционале незаметна. Если вы обнаружите разницу, которая может оказаться проблемой, можно восстановить прежнее поведение, добавив перед интерполированной строкой приведение к (string)
. Например:
stringBuilder.Append((string)$"{a} {b}")
Однако это не рекомендуется, если только не требуется для обеспечения совместимости.