Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/281.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# StringBuilder类是如何实现的?它是否在每次追加时都在内部创建新的字符串对象?_C#_.net_String_Stringbuilder - Fatal编程技术网

C# StringBuilder类是如何实现的?它是否在每次追加时都在内部创建新的字符串对象?

C# StringBuilder类是如何实现的?它是否在每次追加时都在内部创建新的字符串对象?,c#,.net,string,stringbuilder,C#,.net,String,Stringbuilder,StringBuilder类是如何实现的?它是否在每次追加时都在内部创建新的字符串对象?不太可能-它使用内部字符缓冲区。只有当缓冲区容量耗尽时,它才会分配新的缓冲区。Append操作将简单地添加到此缓冲区,当调用ToString()方法时,将创建string对象-从今以后,建议对许多字符串进行串联,因为每个传统的字符串concat操作将创建新字符串。如果您对字符串生成器的初始容量有大致的了解,还可以为其指定初始容量,以避免多次分配 编辑:人们指出我的理解是错误的请忽略答案(我不想删除它-它将证明

StringBuilder类是如何实现的?它是否在每次追加时都在内部创建新的字符串对象?

不太可能-它使用内部字符缓冲区。只有当缓冲区容量耗尽时,它才会分配新的缓冲区。Append操作将简单地添加到此缓冲区,当调用ToString()方法时,将创建string对象-从今以后,建议对许多字符串进行串联,因为每个传统的字符串concat操作将创建新字符串。如果您对字符串生成器的初始容量有大致的了解,还可以为其指定初始容量,以避免多次分配


编辑:人们指出我的理解是错误的请忽略答案(我不想删除它-它将证明我的无知:-)

在.NET 2.0中,它在内部使用
字符串
类<代码>字符串仅在
系统
命名空间之外是不可变的,因此
StringBuilder
可以做到这一点

在.NET 4.0中,
String
已更改为使用
char[]

在2.0中,StringBuilder

public sealed class StringBuilder : ISerializable
{
    // Fields
    private const string CapacityField = "Capacity";
    internal const int DefaultCapacity = 0x10;
    internal IntPtr m_currentThread;
    internal int m_MaxCapacity;
    internal volatile string m_StringValue; // HERE ----------------------
    private const string MaxCapacityField = "m_MaxCapacity";
    private const string StringValueField = "m_StringValue";
    private const string ThreadIDField = "m_currentThread";
但在4.0中,它看起来是这样的:

public sealed class StringBuilder : ISerializable
{
    // Fields
    private const string CapacityField = "Capacity";
    internal const int DefaultCapacity = 0x10;
    internal char[] m_ChunkChars; // HERE --------------------------------
    internal int m_ChunkLength;
    internal int m_ChunkOffset;
    internal StringBuilder m_ChunkPrevious;
    internal int m_MaxCapacity;
    private const string MaxCapacityField = "m_MaxCapacity";
    internal const int MaxChunkSize = 0x1f40;
    private const string StringValueField = "m_StringValue";
    private const string ThreadIDField = "m_currentThread";
所以很明显,它已经从使用
字符串改为使用
char[]


编辑:更新答案以反映.NET 4中的更改(我只是刚刚发现)。

如果我查看.NET 2中的.NET Reflector,我会发现:

public StringBuilder Append(string value)
{
    if (value != null)
    {
        string stringValue = this.m_StringValue;
        IntPtr currentThread = Thread.InternalGetCurrentThread();
        if (this.m_currentThread != currentThread)
        {
            stringValue = string.GetStringForStringBuilder(stringValue, stringValue.Capacity);
        }
        int length = stringValue.Length;
        int requiredLength = length + value.Length;
        if (this.NeedsAllocation(stringValue, requiredLength))
        {
            string newString = this.GetNewString(stringValue, requiredLength);
            newString.AppendInPlace(value, length);
            this.ReplaceString(currentThread, newString);
        }
        else
        {
            stringValue.AppendInPlace(value, length);
            this.ReplaceString(currentThread, stringValue);
        }
    }
    return this;
}
所以它是一个变异的字符串实例


编辑除了在.NET 4中,它是一个
char[]

如果您想看到一个可能的实现(类似于在github上看到的microsoft实现到v3.5版本时附带的实现)。

我制作了一个小示例来演示StringBuilder如何在.NET 4中工作。合同是

public interface ISimpleStringBuilder
{
    ISimpleStringBuilder Append(string value);
    ISimpleStringBuilder Clear();
    int Lenght { get; }
    int Capacity { get; }
}
这是一个非常基本的实现

public class SimpleStringBuilder : ISimpleStringBuilder
{
    public const int DefaultCapacity = 32;

    private char[] _internalBuffer;

    public int Lenght { get; private set; }
    public int Capacity { get; private set; }

    public SimpleStringBuilder(int capacity)
    {
        Capacity = capacity;
        _internalBuffer = new char[capacity];
        Lenght = 0;
    }

    public SimpleStringBuilder() : this(DefaultCapacity) { }

    public ISimpleStringBuilder Append(string value)
    {
        char[] data = value.ToCharArray();

        //check if space is available for additional data
        InternalEnsureCapacity(data.Length);

        foreach (char t in data)
        {
            _internalBuffer[Lenght] = t;
            Lenght++;
        }

        return this;
    }

    public ISimpleStringBuilder Clear()
    {
        _internalBuffer = new char[Capacity];
        Lenght = 0;
        return this;
    }

    public override string ToString()
    {
        //use only non-null ('\0') characters
        var tmp = new char[Lenght];
        for (int i = 0; i < Lenght; i++)
        {
            tmp[i] = _internalBuffer[i];
        }
        return new string(tmp);
    }

    private void InternalExpandBuffer()
    {
        //double capacity by default
        Capacity *= 2;

        //copy to new array
        var tmpBuffer = new char[Capacity];
        for (int i = 0; i < _internalBuffer.Length; i++)
        {
            char c = _internalBuffer[i];
            tmpBuffer[i] = c;
        }
        _internalBuffer = tmpBuffer;
    }

    private void InternalEnsureCapacity(int additionalLenghtRequired)
    {
        while (Lenght + additionalLenghtRequired > Capacity)
        {
            //not enough space in the current buffer    
            //double capacity
            InternalExpandBuffer();
        }
    }
}
公共类SimpleStringBuilder:ISimpleStringBuilder
{
公共const int DefaultCapacity=32;
私有字符[]\u内部缓冲区;
公共整数长度{get;私有集;}
公共整数容量{get;私有集;}
公共SimpleStringBuilder(内部容量)
{
容量=容量;
_internalBuffer=新字符[容量];
长度=0;
}
公共SimpleStringBuilder():此(默认容量){}
公共ISimpleStringBuilder追加(字符串值)
{
char[]data=value.ToCharArray();
//检查是否有空间用于其他数据
内部保证能力(data.Length);
foreach(数据中的字符)
{
_内部缓冲区[长度]=t;
长度++;
}
归还这个;
}
公共ISimpleStringBuilder Clear()
{
_internalBuffer=新字符[容量];
长度=0;
归还这个;
}
公共重写字符串ToString()
{
//仅使用非空('\0')字符
var tmp=新字符[长度];
对于(int i=0;i容量)
{
//当前缓冲区中没有足够的空间
//双容量
内部扩展缓冲区();
}
}
}
此代码不是线程安全的,不进行任何输入验证,并且没有使用System.String的内部(不安全)魔力。不过,它确实演示了StringBuilder类背后的思想


一些单元测试和完整的示例代码可以在中找到。

已接受的答案与分数相差一英里。4.0中对
StringBuilder
的重大更改并不是从不安全的
string
更改为
char[]
——而是
StringBuilder
现在实际上是
StringBuilder
实例的链接列表。


这种更改的原因应该很明显:现在不需要重新分配缓冲区(这是一项昂贵的操作,因为在分配更多内存的同时,还必须将所有内容从旧缓冲区复制到新缓冲区)

这意味着调用
ToString()
现在稍微慢一点,因为需要计算最后的字符串,但是执行大量
Append()
操作现在要快得多。这符合
StringBuilder
的典型用例:大量调用
Append()
,然后一次调用
ToString()



你可以找到基准。结论是什么?新的链表
StringBuilder
使用了更多的内存,但对于典型的用例来说速度要快得多。

不知道。。我想我会做一些反射魔法来满足我的好奇心:)@Brian:据我所知,它在内部持有一个
Char
数组,而不是
String
(至少在.NET 4中,这可能已经改变了?)@Fredrik-在MS实现中,它真的是一个
字符串
mutated@Marc:这让我好奇,所以我用反射器检查;看来这已经改变了。以前它是一个
字符串
,现在它似乎是一个被操纵的
字符
数组。它的行为就像一个字符缓冲区,但实际上它是一个变异的
字符串
实例。老实说。谢谢马克-我觉得它使用了字符缓冲区。这意味着它将有一些本机实现来改变string对象。当然,但是