Dot Net Perls
C#

StringBuilder Secrets

by Sam Allen

Problem

Append characters or strings together repeatedly, yielding a longer, single string. Avoid the severe performance problem with strings when modifying them. For example, build up a single string with StringBuilder from 1,000 small strings (numbers, or characters). Measure performance and determine best practices.

Solution: C#

To start using StringBuilder, you should include the "using System.Text;" namespace in your file. What follows is an example of using StringBuilder with a string we append. (Note that you will almost always use a StringBuilder in a loop.) Look at how string literals are passed to Append.

class Program
{
    static void Main(string[] args)
    {
        //
        // Declare a new StringBuilder with the default constructor.
        //
        StringBuilder builder = new StringBuilder();
        builder.Append("The list starts here:"); // Append some text to it.
        builder.AppendLine(); // Terminate the above text with a line break.

        builder.Append("1 cat").AppendLine(); // Append a string.

        //
        // Get ahold of the StringBuilder's buffer content.
        //
        string innerString = builder.ToString();

        //
        // Display to the screen with debugging class.
        //
        Debug.WriteLine(innerString);
    }
}
  1. Use 'new' keyword for StringBuilder
    You must use the new keyword to make a new StringBuilder. (This is different than how strings are usually used in C#.) It has many overloaded constructors, but usually you only need the empty one.
  2. Append() method called
    Append is a very important method to know. It will add the contents of its arguments to the buffer in the StringBuilder. Keep in mind that every argument to StringBuilder will automatically have its ToString() method called. So there isn't any advantage to using non-string type arguments. (You can simply call ToString on your own.)
  3. AppendLine() used
    Does the exact same thing as Append(), except with a newline on the end. Useful if you don't want to use Environment.NewLine.
  4. Append and AppendLine called on themselves
    This is a really neat way to use a more terse syntax with StringBuilder. It is called a "fluent" interface or method chaining, and it is described in my article about StringBuilder syntax.
  5. ToString returns the buffer
    You will almost always want ToString(). It will return the contents as a string.

Feeling pressured?

You know that StringBuilder will make your appends go faster usually, but I want to note another benefit about StringBuilder. In .NET, there is a concept of "memory pressure", meaning that the more temporary objects created by your app, the more often garbage collection does its thing and relocates memory. Because it creates fewer temporary objects, StringBuilder will reduce memory pressure (and increase performance).

I want to quickly replace or insert characters to my string

Use StringBuilder to do this. First we must convert our string to a StringBuilder, and then we can call StringBuilder's methods to do these operations. This is faster because, again, it is using char[] arrays and not unchangeable strings. Here is an example.

StringBuilder builder = new StringBuilder(
    "This is an example string that is an example.");

builder.Replace("an", "the"); // Replaces 'an' with 'the'.

Debug.WriteLine(builder.ToString());
//
// "This is the example string that is the example."
//

When shouldn't I use StringBuilder?

When your requirements do not involve a loop, generally you should avoid StringBuilder. This is because there is a lot of logic in StringBuilder that will be slow on very small operations. Also, this will make the syntax for your code uglier. Here are a couple gotchas you must consider when you study StringBuilder.

Show me an example with a loop

As I have noted, almost always your StringBuilder will be used in a loop. This can be a foreach, for, or while loop. Right here I want to present another example of StringBuilder, but in a foreach loop. After the code, I show the final string built.

string[] items = { "Cat", "Dog", "Celebrity" };

StringBuilder builder2 = new StringBuilder("These items are required:").AppendLine();

foreach (string item in items)
{
    builder2.Append(item).AppendLine();
}
Debug.WriteLine(builder2.ToString());
// These items are required:
// Cat
// Dog
// Celebrity

Where are the StringBuilder benchmarks?

I don't offer benchmarks of StringBuilder vs. string, as that is obvious. However, next in this article I test a way of using StringBuilder more effectively as a parameter. It is an algorithmic optimization, not a "micro-optimization". Additionally, it is good to remember that StringWriter, and the HtmlTextWriter class, will have the same optimizations because they are based on StringBuilder.

StringBuilder as a method parameter

Here's a tip: use StringBuilder as a method parameter (part of the function signature). This is a significant optimization because it will avoid converting back and forth to strings. What this next code example shows is how you can use StringBuilder parameters and reuse the same StringBuilder. This example compares thousands of StringBuilders to a single one. (Strings alone would be hugely slower.)

/// <summary>
/// Example program class.
/// </summary>
class Program
{
    /// <summary>
    /// Example strings.
    /// </summary>
    static string[] _items = new string[] { "cat", "dog", "emu",
        "giraffe", "politician" };

    /// <summary>
    /// Append to the StringBuilder parameter.
    /// </summary>
    static void AppendToStringBuilder(StringBuilder builderIn)
    {
        foreach (string item in _items)
        {
            builderIn.AppendLine(item); // Append to existing StringBuilder.
        }
    }

    /// <summary>
    /// Append to a new StringBuilder and return it as a string.
    /// </summary>
    static string AppendReturnString()
    {
        StringBuilder builder = new StringBuilder();
        foreach (string item in _items)
        {
            builder.AppendLine(item);
        }
        return builder.ToString(); // Return as string.
    }

    /// <summary>
    /// Example Main method.
    /// </summary>
    static void Main(string[] args)
    {
        long t1 = Environment.TickCount; // Take time.
        for (int i = 0; i < 100000; i++) // 3073 ms.
        {
            StringBuilder builder = new StringBuilder();
            for (int a = 0; a < 100; a++)
            {
                AppendToStringBuilder(builder); // Use as parameter.
            }
        }
        long t2 = Environment.TickCount;
        for (int i = 0; i < 100000; i++) // 5039 ms.
        {
            StringBuilder builder = new StringBuilder();
            for (int a = 0; a < 100; a++)
            {
                builder.Append(AppendReturnString());
            }
        }
        long t3 = Environment.TickCount;

        Console.WriteLine((t3 - t2).ToString() + ", " + (t2 - t1).ToString());
        //
        // --- Results: ---
        // 5039, 3073
    }
}

What does 'immutable' really mean?

Every site will tell you that 'immutable' means something can't be changed. Try to assign to a character in a string, such as |str[0] = 'a'|. You can't do it, because strings cannot be changed. However, character arrays can be changed. StringBuilder uses "mutable" character arrays for its buffer.

I am getting an ArgumentOutOfRangeException

This may be because you are putting too much stuff in your StringBuilder. The maximum number of characters in a StringBuilder (its max capacity) is equal to Int32.MaxValue. If this is happening, check for infinite loops or other really serious problems.

Discussion

We explored the basics of StringBuilder, and drew in some experience with how and when to use it. Pay heed to the mistake I mention above, and note the special syntax you can use. The best way to use StringBuilder is to simply reuse the same one and call Append repeatedly. Finally, always prefer using StringBuilder as a parameter versus using ToString frequently.

© 2008 Sam Allen. All rights reserved.

Ads by The Lounge