Iterative Coding

George Bell15/06/2020

StringBuilder or: How I Learned To Stop Worrying And Love Making New Strings

I'd like you to think back. Back to when you started developing. It doesn't have to be in any particular language, just when you started learning. If you're like me (and I hope you are, otherwise this opening is a bit of a waste), then one of the first things you'll have learnt is how to work with strings. More specifically, how you'd do this:

Console.WriteLine("What is your name?");
var name = Console.ReadLine();
Console.WriteLine("Hello, " + name + ".");

However, once you start dealing with large sets of data you want to convey in a string, things start getting funky. Below is an example of a method that takes some cats that are up for adoption, along with the currency code and the cost of each cat and returns a nice, human-readable string:

public string GenerateCatAdoptionList(IEnumerable<string> cats, char currencySymbol, double costPerCat)
{
    string catMessage = "The cats looking for loving homes are: " + cats.First();
    
    foreach (var cat in cats)
    {
        if (cat == cats.Last())
        {
            catMessage += " and " + cat + ".";    
        }
        else if (cat != cats.First())
        {
            catMessage += ", " + cat;    
        }
    }
    
    catMessage += " Our adoption fee is " + currencySymbol + costPerCat;
    
    return catMessage;
}

Running this will return the following string:

The cats looking for loving homes are: Pickles, Larry, Mr Meow, Matilda and Ace. Our adoption fee is £200


But what about interpolation?

Good question, person who read that out load. Interpolation does have a place in the world and is definitely something that could be used in this example:

public string GenerateCatAdoptionList(IEnumerable<string> cats, char currencySymbol, double costPerCat)
{
    string catMessage = "The cats looking for loving homes are: " + cats.First();
    string catMessage = $"The cats looking for loving homes are: {cats.First()}";
    
    foreach (var cat in cats)
    {
        if (cat == cats.Last())
        {
            catMessage += " and " + cat + ".";
            catMessage += $" and {cat}.";    
        }
        else if (cat != cats.First())
        {
            catMessage += ", " + cat;
            catMessage += $", {cat}";    
        }
    }
    
    catMessage += " Our adoption fee is " + currencySymbol + costPerCat;
    catMessage += $" Our adoption fee is {currencySymbol}{costPerCat}";
    
    return catMessage;
}

Yep, that is a bit better, isn't it. But is there another way?


Enter the StringBuilder

The class StringBuilder is a great way to build up strings (shocking, I know). I like to use it if I'm going to be building up a string (again, shocking) as, unlike a string, StringBuilder is lets you manipulate, add to and replace data in a representation of a string without creating a new string. That's right, string is immutable, so every time you alter one of them, you are in fact creating a new one.

Now, this doesn't mean you should always use a StringBuilder. A lot of the time, a spot of interpolation be just the ticket. Where it really shines is when you are dealing with large datasets, such as a list of cats that you want to herd into a string. On that poor segue, let's take a look at our code re-written to use StringBuilder.

public string GenerateCatAdoptionList(IEnumerable<string> cats, char currencySymbol, double costPerCat)
{
    string catMessage = $"The cats looking for loving homes are: {cats.First()}";
    StringBuilder catMessage = new StringBuilder($"The cats looking for loving homes are: {cats.First()}");
    
    foreach (var cat in cats)
    {
        if (cat == cats.Last())
        {
            catMessage += $" and {cat}.";
            catMessage.Append($" and {cat}.");    
        }
        else if (cat != cats.First())
        {
            catMessage += $", {cat}";
            catMessage.Append($", {cat}");
        }
    }
    
    catMessage += $" Our adoption fee is {currencySymbol}{costPerCat}";
    catMessage.Append($" Our adoption fee is {currencySymbol}{costPerCat}");
    
    return catMessage
    return catMessage.ToString();
}

Now that we have done that, we can start using some of the advanced functionality of StringBuilder. Let's say you want to the adoption fee to appear on a different line to the cats. For that, you actually need to modify the last cat statement:

...
catMessage.Append($" and {cat}.");
catMessage.AppendLine($" and {cat}.");
...

AppendLine adds the string you have passed in and then adds a newline character.

One last thing. When I'm using interpolation, I have a rule:

Using this rule, the line adding the fees becomes:

...
catMessage.Append($"Our adoption fee is {currencySymbol}{costPerCat}");
catMessage.AppendFormat("Our adoption fee is {0}{1}", currencySymbol, costPerCat);
...

AppendFormat is, unsurprisingly, the equivalent to string.Format. I've no evidence to back this up, but I think it reads much nicer.

Now, all being well, we should have only made one change to the output of our method. Shall we take a look at the finished product?

public string GenerateCatAdoptionList(IEnumerable<string> cats, char currencySymbol, double costPerCat)
{
    StringBuilder catMessage = new StringBuilder($"The cats looking for loving homes are: {cats.First()}");
    
    foreach (var cat in cats)
    {
        if (cat == cats.Last())
        {
            catMessage.Append($" and {cat}.");
        }
        else if (cat != cats.First())
        {
            catMessage.AppendLine($" and {cat}.");
        }
    }
    
    catMessage.AppendFormat("Our adoption fee is {0}{1}", currencySymbol, costPerCat);
    
    return catMessage.ToString();
}

The cats looking for loving homes are: Pickles, Larry, Mr Meow, Matilda and Ace.
Our adoption fee is £200

Hooray!

Back home

Copyright © 2020 Russ Penska