我已经创建了测试应用程序来测试,是StringBuilder将数据复制到另一个实例并在其长度超过当前容量时增长缓冲区并在ildasm.exe中验证但它看起来完全相同。
如何验证StringBuilder会将其数据复制到新实例并按指定的限制增长缓冲区?
答案 0 :(得分:11)
Capacity表示分配给StringBuilder的连续内存。容量可以是> =字符串的长度。当StringBuilder附加的数据多于容量时,StringBuilder会自动增加容量。由于容量已超过(即连续内存已填满且没有更多缓冲区可用),因此会分配更大的缓冲区,并将数据从原始内存复制到此新区域。
它不会将数据复制到新的“实例”,而是复制到新的“内存位置”。实例保持不变,但指向新的内存位置。
编辑 仅供参考:如果在创建期间未指定,则StringBuilder的默认容量为16
如果您想查看StringBuilder的内存位置,那么您可以调试应用程序并使用Debug>检查内存。 Windows>记忆。实际上,当Append stmt运行时,您可以看到存储在StringBuilder中的每个字节的地址。
如果您需要以编程方式获取位置this link可能会有所帮助。
答案 1 :(得分:5)
并不是说我们真的测试了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
答案 2 :(得分:5)
如果要检查StringBuilder的实现方式,只需启动Reflector并查看它。 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
等部分,以查找您要查找的内容。
答案 3 :(得分:2)
StringBuilder在需要时增加缓冲区的方式是由StringBuilder的内部代码处理的;它不会显示在您的应用程序的IL代码中;编译器无法知道某个方法中的StringBuilder将包含多大的字符串,因为它可能会不时变化。
但是,当StringBuilder 增加其缓冲区时,它不会产生新的StringBuilder实例。它可能导致它将它所持有的字符串的内部表示复制到一个新实例(我不太了解该类的内部工作,以确切地说明发生了什么)。
答案 4 :(得分:0)
您可以使用Reflector查看StringBuilder的工作原理。
参见方法
StringBuilder Append(string value)
对于.Net 3.5逻辑是这样的:如果缓冲区长度不足以用于新字符串,则创建长度等于Max的新缓冲区(oldSize * 2,requiredSize)。
换句话说,StringBuffer尝试将缓冲区加倍,如果这还不够,那么使缓冲区大小足以适应新字符串。
删除对旧缓冲区的引用,并使用下一个垃圾收集回收旧缓冲区。
答案 5 :(得分:0)
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
}
}