Dot-Net
呼叫 ToString 後,StringBuilder 是否變得不可變?
我清楚地記得在 .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;