调用ToString之后,StringBuilder是否变得不可变? [英] Does StringBuilder become immutable after a call to ToString?
问题描述
我从.NET的早期就清楚地记得,在StringBuilder上调用ToString用来为新的字符串对象(要返回)提供StringBuilder使用的内部字符缓冲区。这样,如果您使用StringBuilder构造了一个巨大的字符串,则调用ToString不必将其复制。
I distinctly remember from the early days of .NET that calling ToString on a StringBuilder used to provide the new string object (to be returned) with the internal char buffer used by StringBuilder. This way if you constructed a huge string using StringBuilder, calling ToString didn't have to copy it.
在执行此操作时,StringBuilder必须防止对字符串进行任何其他更改。缓冲区,因为它现在被不可变的字符串使用。结果,StringBuilder将切换到更改时复制,其中任何尝试的更改都将首先创建一个新缓冲区,将旧缓冲区的内容复制到该缓冲区,然后再进行更改。
In doing that, the StringBuilder had to prevent any additional changes to the buffer, because it was now used by an immutable string. As a result the StringBuilder would switch to a "copy-on-change" made where any attempted change would first create a new buffer, copy the content of the old buffer to it and only then change it.
我认为假设是使用StringBuilder构造一个字符串,然后将其转换为常规字符串并丢弃。对我来说似乎是一个合理的假设。
I think the assumption was that StringBuilder would be used to construct a string, then converted to a regular string and discarded. Seems like a reasonable assumption to me.
现在这是事情。我在文档中找不到任何提及。
Now here is the thing. I can't find any mention of this in the documentation. But I'm not sure it was ever documented.
所以我看着使用Reflector(.NET 4.0)的ToString的实现,在我看来,它实际上是复制字符串,而不只是共享缓冲区:
So I looked at the implementation of ToString using Reflector (.NET 4.0), and it seems to me that it actually copies the string, rather than just share the buffer:
[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。我什至在书。
Now, as I mentioned before I distinctly remember reading that this was the case in the early days if .NET. I even found a mention of in this book.
我的问题是,此行为被删除了吗?如果是这样,有人知道为什么吗?对我来说,这很有意义。
My question is, was this behavior dropped? If so, anyone knows why? It made perfect sense to me...
推荐答案
是的,它已经完全针对.NET 4.0重新设计。现在,它使用绳索(字符串构建器的链接列表)存储不断增长的内部缓冲区。当您无法很好地猜测初始容量并且文本量很大时,这是解决问题的方法。这样会创建大量内部废弃缓冲区的副本,从而阻塞了大型对象堆。源代码中的注释(可从参考源中获得)是相关的:
Yup, this has been completely redesigned for .NET 4.0. It now uses a rope, a linked list of string builders to store the growing internal buffer. This is a workaround for a problem when you can't guess the initial Capacity well and the amount of text is large. That creates a lot of copies of the dis-used internal buffer, clogging up the Large Object Heap. This comment from the source code as available from the Reference Source is relevant:
// 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;
这篇关于调用ToString之后,StringBuilder是否变得不可变?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!