StringBuilder在调用ToString后变得不可变吗?

M.S*_*M.S 15 .net stringbuilder tostring immutability copy-on-write

我清楚地记得从早期的.NET开始,在StringBuilder上调用ToString用于提供新的字符串对象(要返回)与StringBuilder使用的内部char缓冲区.这样,如果使用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;
}
Run Code Online (Sandbox Code Playgroud)

现在,正如我之前提到的,我清楚地记得在早期的情况下读到这种情况,如果是.NET.我甚至在本书中提到了这一点.

我的问题是,这种行为被删除了吗?如果是这样,有谁知道为什么?这对我来说很有意义......

Guf*_*ffa 5

是的,你没记错.将StringBuilder.ToString它作为用于使得所述附加改变用于返回内部缓冲器作为字符串的方法,和标志StringBuilder必须分配一个新的缓冲区.

由于这是一个实现细节,因此文档中未提及.这就是为什么他们可以在不破坏类的已定义行为的任​​何内容的情况下更改底层实现.

正如您从发布的代码中看到的那样,不再有单个内部缓冲区,而是将字符存储在块中,并且该ToString方法将块一起放入字符串中.

实现这种变化的原因很可能是他们已经收集了关于如何StringBuilder实际使用该类的信息,并得出结论,这种方法在平均情况和最差情况之间提供了更好的性能.


Han*_*ant 5

是的,这已经完全重新设计为.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;
Run Code Online (Sandbox Code Playgroud)