More StringBuilder advice


I first wrote about this here, but I’ve since received several requests for more prescriptive advice on the subject.  Again you can never be sure without understanding the exact usage pattern because the results really do vary widely but here’s some general guidelines that may be useful.


The choice (String vs. StringBuilder) usually turns not on whether the strings are known at compile time or not, but rather on whether the number of appends is known.  This is especially true if you can’t really be sure of the sizes in advance either (i.e. they might vary).


e.g.

 

if your pattern looks like:


x = f1(…) + f2(…) + f3(…) + f4(…)

 

that’s one concat and it’s zippy, StringBuilder probably won’t help.

 

if your pattern looks like:


if (…) x += f1(…)

if (…) x += f2(…)

if (…) x += f3(…)

if (…) x += f4(…)

 

then you probably want StringBuilder.

 

Things like generating SQL on the fly for a query probably want StringBuilder, things like making an absolute file path from components probably don’t (unless those components could be put into a buffer that’s part of some bigger operation, thereby saving temporary strings).

 

Don’t underestimate the value of this pattern:


StringBuilder sb;

 


if (…) AppendF1(sb,…)

if (…) AppendF2(sb,…)

if (…) AppendF3(sb,…)

if (…) AppendF4(sb,…)

 


If you pass in the buffer, then the f1 through f4 append functions might not ever need to make temporary string parts, they can stream into the buffer.  And it recurses well.  For large scale string assembly (like making SQL statements on the fly) I find the last pattern very useful.  Reusing the same buffer can be much better than:

 



if (…) sb.Append(f1(…))

if (…) sb.Append(f2(…))

if (…) sb.Append(f3(…))

if (…) sb.Append(f4(…))

Please remember to ignore my advice when you have a thoughtful reason to do so :)

 

Comments (14)

  1. Nicko says:

    I often find that passing a TextWriter to child functions can be more benificial that just passing a StringBuilder.

    If you pass in a StringWriter the effect is the same as passing a StringBuilder.

    If you decide that one time you want the output to go to a file rather than into a string you can do that without creating a temporary string.

  2. what about a construct like:

    x = (… ? f1(…) : "")

    + (… ? f2(…) : "")

    + (… ? f3(…) : "")

    + (… ? f4(…) : "");

    would that still use a single concat or would the ternary expressions screw things up to where a StringBuilder would be faster?

  3. Rico Mariani says:

    Always check the generated code to be sure because even "experts" like me are surprised sometimes, however in this particular case I believe you will still get concat.

  4. Jim Argeropoulos says:

    I often choose string.Format() over chaining strings with the plus operator. Any comments on this?

    I think you are going to tell me that it amounts to a cat, but?

    Thanks

    Jim

    Oh, yeah. One more thing. Reading between the lines, I would guess that a string builder should not be appended to after calling ToString(). Is that a fair statement?

  5. Rico Mariani says:

    String.Format takes the various arguments, builds them into an object array, creates an empty StringBuilder and then passes them along to the StringBuilder.FormatAppend services.

    That will be quite a bit more costly than just the straight append in the easy cases. But it does offer advantages such as localizability and so forth. It’s certainly not a plain cat. You can see this happening if you trace it all with CLRProfiler which I highly recommend.

    Your reading between the lines is quite correct. It’s not such a good idea to use a StringBuilder after you’ve extracted the string because the StringBuilder sort of falls on a sword on your behalf when you take the string out — the internal buffer is converted into an immutable string in place. Further appends basically start from scratch.

  6. Tyson Brown says:

    Quick question on the pattern above: Is is true that VB.NET doesn’t have true ByRefs?

    I had read a while back that VB.NET still makes a copy of object, then another copy of the object to return..

  7. Is there another reason for passing a StringBuilder to a StringWriter’s constructor apart from the fact that you can manipulate the underlying string in the StringBuilder after the StringWriter is closed :

    //E.G.

    StringBuilder sb = new StringBuilder();

    StringWriter writer = new StringWriter(sb);

    Dataset.WriteXml(writer, XmlWriteMode.WriteSchema);

    writer.Close();

    Return sb.ToString();

  8. 杨其明 says:

    1、String创建一个不可改变的对象,StringBuilder创建一个可以改变的对象。stringstr1=

  9. new2008 says:

    String和StringBuilder

    1、String创建一个不可改变的对象,StringBuilder创建一个可以改变的对象。

    stringstr1=