テストするテストアプリケーションを作成しました。StringBuilderはデータを別のインスタンスにコピーし、その長さが現在の容量を超えたときにバッファを拡張し、ildasm.exeで確認しますが、同じように見えます。
StringBuilderがそのデータを新しいインスタンスにコピーし、指定された制限だけバッファを拡張することを確認するにはどうすればよいですか?
テストするテストアプリケーションを作成しました。StringBuilderはデータを別のインスタンスにコピーし、その長さが現在の容量を超えたときにバッファを拡張し、ildasm.exeで確認しますが、同じように見えます。
StringBuilderがそのデータを新しいインスタンスにコピーし、指定された制限だけバッファを拡張することを確認するにはどうすればよいですか?
容量は、StringBuilderに割り当てられた連続したメモリを表します。容量は、文字列の長さ以上にすることができます。容量よりも多くのデータがStringBuilderに追加されると、StringBuilderは自動的に容量を増やします。容量を超えたため(つまり、連続したメモリがいっぱいになり、使用可能なバッファルームがなくなったため)、より大きなバッファ領域が割り当てられ、データが元のメモリからこの新しい領域にコピーされます。
データを新しい「インスタンス」ではなく、新しい「メモリ位置」にコピーします。インスタンスは同じままですが、新しいメモリ位置を指します。
参考までに編集:作成時に指定されていない場合のStringBuilderのデフォルト容量は16です
StringBuilderのメモリ位置を確認したい場合は、[デバッグ]> [Windows]> [メモリ]を使用して、アプリケーションをデバッグし、メモリを確認できます。Append 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.
当然のことながら、合格し、次の出力が得られます。
容量=10長さ=0MaxCapacity = 2147483647 容量=40長さ=30MaxCapacity = 2147483647
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
探しているものを見つけてください。
StringBuilderが必要に応じてバッファーを増やす方法は、StringBuilderの内部コードによって処理されます。アプリケーションのILコードには表示されません。コンパイラは、特定のメソッドのStringBuilderに含まれる文字列の大きさを知ることができません。これは、文字列が時々変化する可能性があるためです。
ただし、StringBuilderがバッファーを増やしても、新しいStringBuilderインスタンスは作成されません。その結果、保持している文字列の内部表現が新しいインスタンスにコピーされる可能性があります(クラスの内部動作については、何が起こるかを正確に説明するのに十分な知識がありません)。
Reflectorを使用して、StringBuilderがどのように機能するかを確認できます。
メソッドを参照してください
StringBuilder Append(string value)
.Net 3.5のロジックは次のとおりです。バッファの長さが新しい文字列に対して十分でない場合は、Max(oldSize * 2、requiredSize)に等しい長さの新しいバッファを作成します。
つまり、StringBufferはバッファを2倍にしようとしますが、それでも不十分な場合は、新しい文字列に収まるだけのバッファサイズになります。
古いバッファへの参照は削除され、古いバッファは次のガベージコレクションで再利用されます。
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
}
}