Поделиться через


Перегрузки 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}")

Однако это не рекомендуется, если только не требуется для обеспечения совместимости.

Затронутые API

См. также