2

In C#, I have a StringBuilder sb to which I am appending numerous times in for-loops.

Is there is a simple method for StringBuilders that spits out the last string that was appended to it?

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
AJSmyth
  • 419
  • 6
  • 13

3 Answers3

5

Nope. You should probably use a list of strings and then later join it to replace the StringBuilder functionality like this:

List<string> strings = new List<string>();
strings.Add("...");
string result = string.Join("", strings);

Then you can access the most recently added string by accessing the last index of the string list or use the Last() LINQ extension like this:

string lastString = strings.Last();
floele
  • 3,668
  • 4
  • 35
  • 51
  • Or you could append strings with an extra character after each one (except the last one) and then use `LastIndexOf()` to extract the last string. Of course at the end you'd have to replace those characters with empty strings. – Kapol Mar 18 '14 at 21:36
  • You mean append to an existing string? That may become a performance problem though depending on the number of elements. – floele Mar 18 '14 at 21:38
  • 3
    If he only needs the last string he should keep using the `StringBuilder` but use another string variable which holds only the last string appended. – Tim Schmelter Mar 18 '14 at 21:43
  • Ah yes, that would indeed also be a simple solution :) – floele Mar 18 '14 at 21:47
0

You could create your own StringBuilder extension method that remembers the last string appended:

public static class StringBuilderExtensions
{
    public static string LastStringAppended { get; private set; }

    public static StringBuilder AppendRemember(this StringBuilder sb, object obj)
    {
        string s = obj == null ? "" : obj.ToString();
        LastStringAppended = s;
        return sb.Append(s);
    }
}

Call it like this

 sb.AppendRemember("hello").AppendRemember(" world");
 string lastString = StringBuilderExtensions.LastStringAppended;

However, note that the last string will be remembered globally. It is not bound to a StringBuilder instance.


If you need to remember the last addition per StringBuilder instance, you can attach an additional "property" to the StringBuilder via a dictionary. I am using this implementation of ObjectReferenceEqualityComparer<T>. It is necessary beacuse StringBuilder overrides Equals and does not use reference equality.

public static class StringBuilderExtensions
{
    private static Dictionary<StringBuilder, string> _lastStrings =
        new Dictionary<StringBuilder, string>(
            new ObjectReferenceEqualityComparer<StringBuilder>());

    public static string LastAppended(this StringBuilder sb)
    {
        string s;
        _lastStrings.TryGetValue(sb, out s);
        return s; // s is null if not found in the dict.
    }

    public static StringBuilder AppendRemember(this StringBuilder sb, object obj)
    {
        string s = obj == null ? "" : obj.ToString();
        _lastStrings[sb] = s;
        return sb.Append(s);
    }
}

Use it like this:

sb.AppendRemember("hello").AppendRemember(" world");
string lastString = sb.LastAppended();
Community
  • 1
  • 1
Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
  • Do you really think that the advantages outweigh the disadvantages? Instead of a static string i would prefer a custom class inheriting from `StringBuilder` or even better, simply use a `string` variable which keeps track of the last appended string. – Tim Schmelter Mar 18 '14 at 21:48
  • `StringBuilder` is a sealed class so cannot be inherited from. I assume this is so that it can be highly optimized in the framework without us mere mortals messing it up. – Stainy Mar 18 '14 at 21:54
  • @Stainy: good point, so you could use a class that holds a `StringBuilder` or no additional class/extension but just a string that keeps track. – Tim Schmelter Mar 18 '14 at 21:57
  • I added an implementation using a dictionary that remembers the last string per `StringBuilder` instance. – Olivier Jacot-Descombes Mar 18 '14 at 22:04
  • @OlivierJacot-Descombes: the methods and the dictionary need to be static. I don't like static in this context. You are opening Pandora's box needlessly. – Tim Schmelter Mar 18 '14 at 22:12
0

As floele answered, you're probably better off just using a List and then joining it. But as Olivier Jacot-Descombes and Tim Schmelter hinted at in their commeents, you can have your own class with an internal StringBuilder, then you just have to recreate the methods from StringBuilder you want to use, and store the last appended string inside your class in a property. This has two key advantages over Olivier's answer: the remembered string isn't "global" (it's unique to each instance), and the code is many times simpler.

public class StringBuilderRemember
{
    private StringBuilder sb=new StringBuilder();
    public string LastString {get; private set;}

    public void AppendLine(string s)
    {
        LastString=s;
        sb.AppendLine(s);
    }
    public void Append(string s)
    {
        LastString=s;
        sb.Append(s);
    }
    public string ToString()
    {
    return sb.ToString();
    }
}
mason
  • 31,774
  • 10
  • 77
  • 121