InplaceStringBuilder 和 StringBuilder 有什么区别?

Whats the difference between InplaceStringBuilder and StringBuilder?

今天,当我尝试输入 StringBuilder 时,VS2017 智能感知出现 InplaceStringBuilderInplaceStringBuilder 对我来说是新手,所以我开始挖掘,看看我能学到什么。

我注意到的第一件事是它是一个结构而不是 class 并且它的类型信息如下所示:

#region Assembly Microsoft.Extensions.Primitives, Version=1.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
// C:\Users\Ron Clabo\Documents\Visual Studio 2017\Projects\wwwGiftOasisResponsive\packages\Microsoft.Extensions.Primitives.1.1.0\lib\netstandard1.0\Microsoft.Extensions.Primitives.dll
#endregion

using System.Diagnostics;

namespace Microsoft.Extensions.Primitives {
    [DebuggerDisplay("Value = {_value}")]
    public struct InplaceStringBuilder {
        public InplaceStringBuilder(int capacity);

        public int Capacity { get; set; }

        public void Append(string s);
        public void Append(char c);
        public override string ToString();
    }
}

所以它的方法比StringBuilder少很多。然后我四处搜索以了解有关 InplaceStringBuilder 的更多信息,但网络上还没有太多关于它的信息,所以它看起来很新。

另外,区别我已经说了,InplaceStringBuilderStringBuilder有什么区别;开发人员什么时候应该使用新的 InplaceStringBuilder 而不是旧的 StringBuilder

当生成的字符串比初始容量长时,正常的StringBuilder增加容量。 InplaceStringBuilder 受其容量限制,如果结果字符串较长,则会抛出异常。

这是InplaceStringBuilder的一个很大的限制,所以它可能只适用于极少数情况。此外,如果您提前知道容量,则已经可以定义普通StringBuilder.

的初始容量

来源:查看 InplaceStringBuilderGitHub and compare to StringBuilder on MSDN

上的实施

因为它只进行一次分配,所以 InplaceStringBuilder 对于众所周知的、大小合理的字符串更有效。我们可能希望在需要非常高效的方法中使用它。

它是在 pull request #157 中引入的,其中包括以下评论。

Intended to be used instead of pooled StringBuilder or string.Concat when all parts of string are known... Does only 1 allocation of resulting string... should only be used for well-known reasonably sized strings. For everything else, use StringBuilder... do not use across await points...

PR 的历史讲述了这个故事:

  1. 2016 年 7 月,Issue #676 注意到不必要的分配。
  2. 2016 年 9 月,Pull request #699 解决了问题 #676 并建议分解出 "inplace string formatting into a struct..."
  3. 2016 年 9 月,Issue #717 正式确定提案。
  4. 2016 年 9 月,Pull request #157 实施提案。

InplaceStringBuilder 是一种通过附加块来构建字符串的非常快速的方法,前提是您事先知道最终字符串的长度。它的工作原理是预先分配一个固定大小的 string,然后 改变 该字符串(不安全地,通过指针),因为您将块附加到构建器。当你调用 ToString 时,预先分配的字符串,现在充满了数据,直接返回而不需要复制。 (StringBuilder 在您调用 ToString 时复制其内容。)

字符串通常是不可变的,因此您最好确保不要在调用 ToString 之后调用 Append 来改变字符串。 InplaceStringBuilder 试图通过仅允许追加(您不能倒回并写入已追加的字符串的一部分)并要求您在调用 ToString 之前填充整个字符串来确保安全.

然而,InplaceStringBuilder 是一个可变结构,这意味着它通过复制传递。如果您复制构建器(例如,通过将其作为参数传递),副本可能会与原始文件不同步。具体来说,副本的 _offset 字段跟踪到目前为止已附加了多少个字符(以便知道在何处写入下一个 Append 调用),它可能指的是字符串中的某个位置原著已经写好了

也就是说,InplaceStringBuilder不安全并且使用起来有风险。如果处理不当,您可能会破坏 string 最重要的属性之一,即不变性。确保您知道自己在做什么!