views:

893

answers:

5

I have created test application to test, Is StringBuilder copy data to another instance and grow it buffer when its length is more than current capacity and verify in ildasm.exe but it seem identical.

How to verify StringBuilder will copy its data into new instance and grow the buffer by the specified limit?

+2  A: 

The way that the StringBuilder increases its buffer when needed is something that is taken care of by the internal code of the StringBuilder; it will not show in the IL code of your application; the compiler cannot know how large strings the StringBuilder in a certain method will contain, since that can vary from time to time.

However, when the StringBuilder does increase its buffer, it will not result in a new StringBuilder instance. It may result in it copying the internal representation of the string that it holds to a new instance (I don't know enough of the inner workings of the class to say exactly what happens).

Fredrik Mörk
+3  A: 

Capacity represents the contiguous memory allocated to the StringBuilder. Capacity can be >= length of the string. When more data is appended to the StringBuilder than the capacity, StringBuilder automatically increases the capacity. Since the capacity has exceeded (that is contiguous memory is filled up and no more buffer room is available), a larger buffer area is allocated and data is copied from the original memory to this new area.

It does not copy data to new 'instance' but to new 'memory location'. The instance remains the same but points to the new memory location.

Edit FYI: Default capacity of StringBuilder if not specified during creation is 16

If you wanna see the memory locations for StringBuilder then you can debug your applications and check the memory using Debug > Windows > Memory. You can actually see the address of each and every byte stored in your StringBuilder when Append stmt runs.

If you need to get the locations programatically this link might help.

Rashmi Pandit
+2  A: 

Not that we're really testing that StringBuilder works, because it does, but for your own enjoyment you can always write a unit test.

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.

Unsurprisingly, it passes, and the following output is given.

    Capacity = 10 Length = 0 MaxCapacity = 2147483647
    Capacity = 40 Length = 30 MaxCapacity = 2147483647
Robert Paulson
A: 

You can use Reflector to see pretty much how StringBuilder works.

See method

StringBuilder Append(string value)

For .Net 3.5 logic is this: if buffer length is not enough for the new string, create new buffer with length equal to Max(oldSize * 2, requiredSize).

In other words, StringBuffer tries to double the buffer and if that is not enough then makes buffer size just enough to fit the new string.

Reference to the old buffer gets removed and the old buffer gets reclaimed with the next garbage collection.

Konstantin Spirin
+1  A: 

If you want to inspect how StringBuilder is implemented, just fire up Reflector and look at it. The implementation for StringBuilder.Append(string) is as follows

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;
}

Look at the section with NeedsAllocation, GetNewString and so forth to find what you're looking for.

Brian Rasmussen