StringBuilder在调用ToString()方法时,是否会缓存结果字符串?

4
StringBuilder在调用ToString时是否会缓存字符串?例如,这段代码会创建两个不同的内存字符串吗,还是只使用一个:
var sb = new StringBuilder();
sb.Append("foo");
sb.Append("bar");

var str1 = sb.ToString();
var str2 = sb.ToString();

它会缓存连续的读取操作结果吗?

嗯,有什么不清楚的吗?这是一个简单的缓存问题。 - eocron
3
为什么要询问,直接检查ReferenceEquals(str1, str2)即可。 - René Vogt
您还可以检查类中 ToString 的实现:https://referencesource.microsoft.com/#mscorlib/system/text/stringbuilder.cs - Pikoh
1
@RenéVogt 说实话,这可能展示了一种缓存的方式,另一个选项是计算结果被缓存并返回一个新对象。它们也不是字面量,所以除非它们被特别地内部化,否则它们不会是同一个对象。 - David Pilkington
5个回答

8

查看 StringBuilderToString() 源代码,答案是否定的。

    [System.Security.SecuritySafeCritical]  // auto-generated
    public override String ToString() {
        Contract.Ensures(Contract.Result<String>() != null);

        VerifyClassInvariant();

        if (Length == 0)
            return String.Empty;

        string ret = string.FastAllocateString(Length);
        StringBuilder chunk = this;
        unsafe {
            fixed (char* destinationPtr = ret)
            {
                do
                {
                    if (chunk.m_ChunkLength > 0)
                    {
                        // Copy these into local variables so that they are stable even in the presence of ----s (hackers might do this)
                        char[] sourceArray = chunk.m_ChunkChars;
                        int chunkOffset = chunk.m_ChunkOffset;
                        int chunkLength = chunk.m_ChunkLength;

                        // Check that we will not overrun our boundaries. 
                        if ((uint)(chunkLength + chunkOffset) <= ret.Length && (uint)chunkLength <= (uint)sourceArray.Length)
                        {
                            fixed (char* sourcePtr = sourceArray)
                                string.wstrcpy(destinationPtr + chunkOffset, sourcePtr, chunkLength);
                        }
                        else
                        {
                            throw new ArgumentOutOfRangeException("chunkLength", Environment.GetResourceString("ArgumentOutOfRange_Index"));
                        }
                    }
                    chunk = chunk.m_ChunkPrevious;
                } while (chunk != null);
            }
        }
        return ret;

2

StringBuilder类的确切实现如下:

public override String ToString()
{
    Contract.Ensures(Contract.Result<String>() != null);

    VerifyClassInvariant();

    if (Length == 0)
        return String.Empty;

    string ret = string.FastAllocateString(Length);
    StringBuilder chunk = this;
    unsafe
    {
        fixed (char* destinationPtr = ret)
        {
            do
            {
                if (chunk.m_ChunkLength > 0)
                {
                    // Copy these into local variables so that they are stable even in the presence of ----s (hackers might do this)
                    char[] sourceArray = chunk.m_ChunkChars;
                    int chunkOffset = chunk.m_ChunkOffset;
                    int chunkLength = chunk.m_ChunkLength;

                    // Check that we will not overrun our boundaries. 
                    if ((uint)(chunkLength + chunkOffset) <= ret.Length && (uint)chunkLength <= (uint)sourceArray.Length)
                    {
                        fixed (char* sourcePtr = sourceArray)
                            string.wstrcpy(destinationPtr + chunkOffset, sourcePtr, chunkLength);
                    }
                    else
                    {
                        throw new ArgumentOutOfRangeException("chunkLength", Environment.GetResourceString("ArgumentOutOfRange_Index"));
                    }
                }
                chunk = chunk.m_ChunkPrevious;
            } while (chunk != null);
        }
    }
    return ret;
}

正如您所看到的,它返回一个字符串,其中包含在该方法内声明的名称为ret的变量...没有被缓存任何地方...


0

我认为是的,因为它将是两个变量,并且您正在对字符串构建器调用tostring

var str1 = sb.ToString();//one variable and new string
var str2 = sb.ToString();//two variable and new string 

我的意思是每次都会创建一个新的字符串。

示例代码:

static void Main(string[] args)
        {

            var sb = new StringBuilder();
            sb.Append("foo");
            sb.Append("bar");

            var str1 = sb.ToString();
            var str2 = sb.ToString();

            Console.WriteLine(str1);
            Console.WriteLine(str2);
            str1 += " str1";
            str2 += " str2";

            Console.WriteLine(str1);
            Console.WriteLine(str2);

            Console.ReadLine();
        }

输出:

foobar
foobar
foobar str1
foobar str2

0

str1和str2是两个分别包含"foo"和"bar"的实例。第一次调用ToSring()时,与第二次调用它时所执行的操作相同,在这两次调用之间没有缓存任何内容,尽管我认为他们本来可以这样做。但您也可以使用str2 = str1。在这两种情况下,str1和str2都是位于不同内存区域中的两个独立的不可变字符串。


-1

在你的情况下,会在内存中创建两个不同的字符串

var str1 = sb.ToString(); // 1st string    
var str2 = sb.ToString(); // 2nd string

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接