0
StringBuilder sb = new StringBuilder("test", 4);
sb.Append('\n');
sb.AppendLine("test1");
sb.AppendLine("test2");
sb.AppendLine("test3");
sb.AppendLine("test4");

looking on IL code there is only one newobj line, but I thought there should be more instances of StringBuilder class since it should increase its capacity by creating new object? Or I got it wrong?

// [3 1 - 3 49]
    IL_0000: ldstr        "test"
    IL_0005: ldc.i4.4
    IL_0006: newobj       instance void [System.Runtime]System.Text.StringBuilder::.ctor(string, int32)
    IL_000b: stloc.0      // sb

    // [4 1 - 4 17]
    IL_000c: ldloc.0      // sb
    IL_000d: ldc.i4.s     10 // 0x0a
    IL_000f: callvirt     instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::Append(char)
    IL_0014: pop

    // [5 1 - 5 24]
    IL_0015: ldloc.0      // sb
    IL_0016: ldstr        "test1"
    IL_001b: callvirt     instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::AppendLine(string)
    IL_0020: pop

    // [6 1 - 6 24]
    IL_0021: ldloc.0      // sb
    IL_0022: ldstr        "test2"
    IL_0027: callvirt     instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::AppendLine(string)
    IL_002c: pop

    // [7 1 - 7 24]
    IL_002d: ldloc.0      // sb
    IL_002e: ldstr        "test3"
    IL_0033: callvirt     instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::AppendLine(string)
    IL_0038: pop

    // [8 1 - 8 24]
    IL_0039: ldloc.0      // sb
    IL_003a: ldstr        "test4"
    IL_003f: callvirt     instance class [System.Runtime]System.Text.StringBuilder [System.Runtime]System.Text.StringBuilder::AppendLine(string)
    IL_0044: pop
    IL_0045: ret

from Troelsen book about c#, translated paragraph by me: "If you add more characters than the specified limit, the StringBuilder object will copy its data to a new instance and increase the buffer size by the specified limit."

Asked a friend to provide original quote, here it is, page 89 "Pro C#10 with .Net 6":

If you append more characters than the specified limit, the StringBuilder object will copy its data into a new instance and grow the buffer by the specified limit.

9
  • 1
    Very unclear why do you think that the code shown would result in creation of multiple string builder instances. Can you elaborate? (I think it is somewhat safe to completely drop IL part till you figure out why/where you expect new string builder to be create in the C# code). Please edit post with that info. Commented Mar 7, 2023 at 1:02
  • I'm not sure what you are expecting? What do you mean by "more exemplars of StringBuilder class"? You mean instances of StringBuilder? Commented Mar 7, 2023 at 1:04
  • 2
    Either that book is wrong or your translation is poor. Increasing the size of a StringBuilder beyond its current Capacity won't create a new StringBuilder object. How could it? You wouldn't have a reference to it afterwards so you couldn't use it. What actually happens is new memory is allocated to store the additional text and that new memory is referenced by the existing StringBuilder object. Commented Mar 7, 2023 at 1:10
  • 1
    Okay, just checked the source code, it apparently does create new StringBuilders, but it's a very obscure implementation detail, so I'm not sure if this is what the book is referring to. Either way, this is not something that you can see by inspecting the IL of your C# code. Commented Mar 7, 2023 at 1:18
  • 1
    @Sweeper you should post that as an answer... I found the same quote can be found in Pro C# 7... Agree that this is very confusing way to explain what is going on... Commented Mar 7, 2023 at 1:37

1 Answer 1

5

Looking at your own C# code's IL will not tell you anything about what StringBuilder does internally, which is what the book is talking about. You would need to look at StringBuilder's source code, or its IL to see that.

The documentation of StringBuilder only mentions "new buffers" or "allocating new memory", and never specifically "new instances of StringBuilder".

New data is appended to the buffer if room is available; otherwise, a new, larger buffer is allocated, data from the original buffer is copied to the new buffer, and the new data is then appended to the new buffer.

If the number of added characters causes the length of the StringBuilder object to exceed its current capacity, new memory is allocated, the value of the Capacity property is doubled, new characters are added to the StringBuilder object, and its Length property is adjusted.

So whether or not it creates new instances of StringBuilders is an implementation detail. Looking at source.dot.net, it does indeed do that in ExpandByABlock.

// Allocate the array before updating any state to avoid leaving inconsistent state behind in case of out of memory exception
char[] chunkChars = GC.AllocateUninitializedArray<char>(newBlockLength);

// Move all of the data from this chunk to a new one, via a few O(1) reference adjustments.
// Then, have this chunk point to the new one as its predecessor.
m_ChunkPrevious = new StringBuilder(this);
m_ChunkOffset += m_ChunkLength;
m_ChunkLength = 0;

m_ChunkChars = chunkChars;

In this implementation, StringBuilder is implemented as a linked list. The StringBuilder instances are nodes. The above code basically sets the previous node to a copy of this, and make this an "empty" node with a capacity of newBlockLength.

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you for the detailed answer! Am I right the phrase could be changed in a more precise way to not confuse very beginners like me? Other than that the book is great
@HumbleNewbie Assuming it actually is referring to this implementation detail, I think it shouldn't mention it at all. Knowing this isn't useful in almost all cases. Alternatively, it could say something similar to the wording in the documentation.
@HumbleNewbie Though you seemed to be confused by a separate issue, that you can somehow observe the internal workings of StringBuilder by inspecting your own code's IL. That isn't really the book's fault...
This is fascinating to me because I never realized that StringBuilder is actually implemented as a linked list and I never would have expected it. It makes a lot more sense now why there are so many advantages to the internal ValueStringBuilder type...not the least of which is that it doesn't need to track StringBuilder node references.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.