Dot-Net

呼叫 ToString 後,StringBuilder 是否變得不可變?

  • November 12, 2010

我清楚地記得在 .NET 的早期,在 StringBuilder 上呼叫 ToString 用於為新字元串對象(要返回)提供 StringBuilder 使用的內部字元緩衝區。這樣,如果您使用 StringBuilder 構造了一個巨大的字元串,呼叫 ToString 就不必複製它。

在這樣做時,StringBuilder 必須防止對緩衝區進行任何額外的更改,因為它現在被一個不可變的字元串使用。結果,StringBuilder 將切換到“更改時複製”,其中任何嘗試的更改都將首先創建一個新緩衝區,將舊緩衝區的內容複製到它,然後才對其進行更改。

我認為假設是 StringBuilder 將用於構造一個字元串,然後轉換為正常字元串並丟棄。對我來說似乎是一個合理的假設。

現在事情就是這樣。我在文件中找不到任何提及。但我不確定它是否被記錄在案。

所以我查看了使用 Reflector (.NET 4.0) 的 ToString 的實現,在我看來它實際上是複製字元串,而不是僅僅共享緩衝區:

[SecuritySafeCritical]
public override unsafe string ToString()
{
   string str = string.FastAllocateString(this.Length);
   StringBuilder chunkPrevious = this;
   fixed (char* str2 = ((char*) str))
   {
       char* chPtr = str2;
       do
       {
           if (chunkPrevious.m_ChunkLength > 0)
           {
               char[] chunkChars = chunkPrevious.m_ChunkChars;
               int chunkOffset = chunkPrevious.m_ChunkOffset;
               int chunkLength = chunkPrevious.m_ChunkLength;
               if ((((ulong) (chunkLength + chunkOffset)) > str.Length) ||     (chunkLength > chunkChars.Length))
               {
                   throw new ArgumentOutOfRangeException("chunkLength",     Environment.GetResourceString("ArgumentOutOfRange_Index"));
               }
               fixed (char* chRef = chunkChars)
               {
                   string.wstrcpy(chPtr + chunkOffset, chRef, chunkLength);
               }
           }
           chunkPrevious = chunkPrevious.m_ChunkPrevious;
       }
       while (chunkPrevious != null);
   }
   return str;
}

現在,正如我之前提到的,我清楚地記得讀過早期的 .NET 就是這種情況。我什至在這本書中找到了提及。

我的問題是,這種行為是否被放棄了?如果是這樣,有人知道為什麼嗎?這對我來說很有意義……

是的,這已針對 .NET 4.0 進行了完全重新設計。它現在使用一個繩子,一個字元串建構器的鍊錶來儲存不斷增長的內部緩衝區。當您無法很好地猜測初始容量並且文本量很大時,這是一種解決方法。這會創建大量未使用的內部緩衝區的副本,從而堵塞大對象堆。來自參考源的原始碼中的此評論是相關的:

   // We want to keep chunk arrays out of large object heap (< 85K bytes ~ 40K chars) to be sure.
   // Making the maximum chunk size big means less allocation code called, but also more waste 
   // in unused characters and slower inserts / replaces (since you do need to slide characters over
   // within a buffer).
   internal const int MaxChunkSize = 8000;

引用自:https://stackoverflow.com/questions/4166155