StringBuilder.Append 重载和计算顺序

C# 10 添加了对更好的字符串内插的支持,包括除字符串外还可以面向自定义“处理程序”。 StringBuilder 通过可接受自定义内插字符串处理程序的新重载 AppendAppendLine 来利用这一功能。 对这些方法的现有调用现在可能会开始绑定到新的重载。 一般情况下,行为相同但性能已提高。 例如,内插字符串的各个组件将直接追加到生成器,而不是先创建一个字符串,然后再追加该字符串。 但是,这可能会更改用作格式项的对象的计算顺序,这可表现为行为差异。

旧行为

在之前的版本中,对此项的调用:

stringBuilder.Append($"{a} {b}");

会编译为此项的等效项:

stringBuilder.Append(string.Format("{0} {1}", a, b));

这意味着系统将先后计算 ab,然后根据这些计算的结果创建一个字符串,接下来将该字符串追加到生成器。

新行为

从 .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 并追加到生成器。

例如,如果 ab 本身是生成器,如以下代码所示,则新的计算顺序可能会导致运行时出现不同的行为。

stringBuilder.Append($"{a} {stringBuilder}");

引入的版本

6.0 RC 1

中断性变更的类型

此项更改可能会影响源兼容性

更改原因

开发人员通常将内插字符串传递给 StringBuilder,因为这比手动拆分字符串并针对每个部分调用 StringBuilder.Append 更方便。 这些新重载可实现简洁的语法以及执行单独调用的大部分性能。

在使用 StringBuilder.AppendStringBuilder.AppendLine 的大多数情况下,你都不会注意到功能差异。 如果发现存在问题的差异,可以通过在内插字符串之前添加 (string) 强制转换来还原以前的行为。 例如:

stringBuilder.Append((string)$"{a} {b}")

除非需要这样做来实现兼容,否则不建议这样做。

受影响的 API

另请参阅