调用ToString之后,StringBuilder是否变得不可变? [英] Does StringBuilder become immutable after a call to ToString?

查看:201
本文介绍了调用ToString之后,StringBuilder是否变得不可变?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我从.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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆