我清楚地记得在 .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 就是这种情况。我什至在这本书中找到了提及。
我的问题是,这种行为是否被放弃了?如果是这样,有人知道为什么吗?这对我来说很有意义......