【问题标题】:Does a StringBuilder initialized with a string contain exactly (only) enough space for that string?用字符串初始化的 StringBuilder 是否包含(仅)足够的空间用于该字符串?
【发布时间】:2010-02-15 13:53:16
【问题描述】:

我想知道这段代码是否......

StringBuilder sb = new StringBuilder("Please read the following messages.");

... 使用与传递给构造函数的字符串一样大的缓冲区初始化sb。一方面,这似乎是最合乎逻辑的事情。另一方面,它似乎违背了StringBuilder 类最常见用途之一的目的,即提供可变性以使重复附加更有效。 (第一次致电Append,如果我的问题的答案是“是”,则需要sb 自行调整大小。)

再说一次,我想这类似于List<T> 的构造函数,它以IEnumerable<T> 作为参数。也许这种情况下的假设是您不打算追加很多,而是操纵已经存在的东西。

我对此做的唯一真正的研究是检查MSDN documentation on StringBuilder,它没有提供答案(它说构造函数“使用指定的字符串”初始化实例,但不表示 如何使用字符串)。


编辑:所以它是“特定于实现的”...这对其他人来说似乎不奇怪吗?我的意思是,StringBuilder 类的目的是提供一种替代方法来在 string 上执行大量操作,同时创建大量不可变的 string 实例;因此,它是为了效率。我觉得应该指定这个构造函数的行为,以便开发人员可以做出明智的决定,无论平台如何使用它。

我的意思是,它由微软以某种方式实现的;他们可以轻松地将其放入文档中(迫使其他实现效仿)。只是个人困惑的来源......

【问题讨论】:

    标签: .net constructor buffer stringbuilder


    【解决方案1】:

    这是一个您无需担心的实现细节。但是,使用.NET reflector,并查看构造函数(其他构造函数调用)的 (string,int32,int32,int32) 重载,我们可以看到它选择的容量是 16 的倍数(下一个大于要求的大小)

    编辑

    实际上是 16 x 2^n,“n”的值被选为下一个最大尺寸

    【讨论】:

    • 好电话——我自己检查了这个并得出了相同的结论(通过在初始化后检查Capacity 属性......不知何故,直到我发布问题后我才发生这种情况)。
    • ...但你不应该依赖这个。如果您足够关心在 Stack Overflow 上发布问题所需的容量,您应该只使用可以指定它的重载之一。 “特定于实现”的整个目的意味着它可以在未来发生变化。不要依赖无证行为。
    • @Lasse - 哦,同意。我不主张依赖这个细节。 (并且努力思考如何最终依赖这个事实)
    • @Lasse:现在我知道问题的答案,我不会。我真正要问的是,如果没有指定它的实现,为什么这个构造函数存在。
    • 这只是它的部分行为没有记录,在 .NET 运行时中有很多。
    【解决方案2】:

    检查 StringBuilder 的容量成员。

    来自MSDN

    StringBuilder 会在需要时动态分配更多空间并相应地增加容量。出于性能原因,StringBuilder 可能会分配比需要更多的内存。分配的内存量是特定于实现的。

    【讨论】:

      【解决方案3】:

      您链接到的构造函数可能链接到StringBuilder(String, Int32, Int32, Int32)

      public StringBuilder(
        string value,
        int startIndex,
        int length,
        int capacity
      )
      

      所以,对于字符串,它可能会通过:string, 0, string.Length, string.Length。或在 StringBuilder 上下文中有意义的类似内容。

      【讨论】:

      • 听起来(来自 Damien 的回答,他使用了 Reflector)你的第一个“可能”是正确的,而你的第二个很接近(实际上是 16 的下一个幂,而不是 string.Length) .
      • ...“16 的下一个幂”是指“2 的下一个幂的 16 倍”。我不认为在 16 之后它会直接到 256,然后到 4096(我很愚蠢)。
      【解决方案4】:

      最终被调用的构造函数是:

      // "Please read the following messages.".Length = 35
      public StringBuilder(string value, int startIndex, int length, int capacity)
      public StringBuilder("Please read the following messages.", 0, 
              "Please read the following messages.".Length, 16)
      

      (这不是其他答案没有提供的,只是来自反射器)
      如果容量小于字符串的长度,在这种情况下是:

      while (capacity < length)
      {
          capacity *= 2;
          if (capacity < 0)
          {
              capacity = length;
              break;
          }
      }
      

      在 Mono 中,StringBuilder(string val) 构造函数将容量分配给 int.MaxValue,直到发生追加。

      真正的答案在于最终在 CLR 内部调用的方法,其中 length 是容量:

      [MethodImpl(MethodImplOptions.InternalCall)]
      private static extern string FastAllocateString(int length);
      

      我在SSCLI 中找不到它的来源,但是 Mono 版本 (\mono\metadata\object.c) 是这样的:

      mono_string_new_size (MonoDomain *domain, gint32 len)
      {
          MonoString *s;
          MonoVTable *vtable;
          size_t size = (sizeof (MonoString) + ((len + 1) * 2));
      
      ...
      }
      

      MonoString 对象的字节大小加上长度乘以 2。

      【讨论】:

      • 这些信息是从哪里来的?您使用的是 Windows、Mono 还是...?您对 StringBuilder 初始容量的发现似乎与 Damien(和我的)不同。
      • @Dan - 0x10 的一个小错误 :)
      • 您提供的代码很有帮助,但与您所说的有冲突! “构造函数使用字符串的长度作为容量”——这不是真的; while 循环翻倍 capacity 直到 capacity &gt;= length;因此它将是它的起始值(看起来是 16)乘以 2 的幂。
      • 错了,我已经改正了,但很想直接点击删除
      • 请不要——现在的答案非常有用。这个特定实现的工作原理变得完全清楚(调用string, int, int, int 构造函数,它将capacity(从16)翻倍,直到达到/超过length,设置为value.Length)。
      猜你喜欢
      • 2016-07-27
      • 2015-04-22
      • 1970-01-01
      • 2018-09-27
      • 2012-01-07
      • 2011-01-25
      • 2018-12-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多