题
我已经创建了测试应用程序进行测试,是StringBuilder将数据复制到另一个实例,并在其长度大于当前容量并在Ildasm.exe中进行验证,但似乎相同。
如何验证StringBuilder将将其数据复制到新实例中,并通过指定的限制来增长缓冲区?
解决方案
如果要检查如何实现StringBuilder,只需启动反射器并查看它。实施 StringBuilder.Append(string)
如下
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;
}
看看该部分 NeedsAllocation
, GetNewString
等等,以找到您要寻找的东西。
其他提示
容量代表分配给StringBuilder的连续内存。容量可以为> =字符串的长度。当将更多的数据附加到StringBuilder上时,StringBuilder会自动增加容量。由于容量已经超过(这是连续的内存填充,并且没有更多的缓冲室),因此分配了较大的缓冲区区域,并将数据从原始内存复制到该新区域。
它不会将数据复制到新的“实例”,而将数据复制到新的“内存位置”。实例保持不变,但指向新的内存位置。
编辑FYI:StringBuilder的默认容量如果未在创建期间指定为16
如果您想查看StringBuilder的内存位置,则可以调试应用程序,并使用调试> Windows> Memory检查内存。实际上,当附加STMT运行时,您实际上可以看到存储在StringBuilder中的每个字节的地址。
如果您需要以编程方式获取位置 这个链接 可能有帮助。
并不是说我们真的在测试StringBuilder的工作原理,因为它确实如此,但是为了您自己的乐趣,您可以随时编写单元测试。
StringBuilder sb = new StringBuilder(10);
Console.WriteLine("Capacity = " + sb.Capacity + " Length = " + sb.Length
+ " MaxCapacity = " + sb.MaxCapacity);
sb.Append("1234567890");
sb.Append("1234567890");
sb.Append("1234567890");
Console.WriteLine("Capacity = " + sb.Capacity + " Length = " + sb.Length
+ " MaxCapacity = " + sb.MaxCapacity);
Assert.AreEqual("123456789012345678901234567890"
, sb.ToString()); // NUnit assert.
毫不奇怪,它通过了,并给出了以下输出。
Capacity = 10 Length = 0 MaxCapacity = 2147483647 Capacity = 40 Length = 30 MaxCapacity = 2147483647
StringBuilder在需要时增加缓冲区的方式是由StringBuilder的内部代码所照顾的;它不会在您的应用程序的IL代码中显示;编译器不知道某种方法中的字符串构造器将包含多大的字符串,因为这些字符串会不时变化。
但是,当弦乐器 做 增加其缓冲区,不会导致新的StringBuilder实例。它可能会导致它复制其包含的字符串的内部表示形式,将其保留到新实例(我对班级的内部工作不足以说明发生了什么)。
您可以使用反射器来查看StringBuilder的工作原理。
参见方法
StringBuilder Append(string value)
对于.NET 3.5逻辑是:如果缓冲区长度不足以适合新字符串,请创建与最大长度等于最大的新缓冲区(oldsize * 2,sircessize)。
换句话说,StringBuffer试图将缓冲区加倍,如果这还不够,则可以使缓冲区大小足以适合新字符串。
对旧缓冲区的引用将被删除,并通过下一个垃圾收集收回旧的缓冲区。
class Program
{
static void Main()
{
StringBuilder sb = new StringBuilder();
Console.WriteLine(sb.Capacity); //16
for (int i = 0; i < 50; i++)
sb.Append(i + ",");
Console.WriteLine(sb.Capacity); //256
sb = new StringBuilder();
Console.WriteLine(sb.Capacity); //16
}
}