How to reuse StringBuilder obj?
Asked Answered
K

3

7

As I know if i declared a dictionary, i could call myDict.Clear() for reusing purpose.

Now if I declared a sb as a StingBuilder obj.

StringBuilder sb = new StringBuilder();

How to reuse sb? thank you.

Acturally i need print all the possible conditions for mainDict.

one of sb expression like this(inclued in the code below)

sb.AppendFormat("{0}/{1}/{2}/{3}, {4}", pair1.Key, pair2.Key, pair3.Key, pair4.Key, pair4.Value);
Console.WriteLine(sb.ToString());

If i declared a lot of StringBuilder objs, i still can't detect how many objs is enough for me. acturally the mainDict is very complex. The code above is a practice only. thanks.


Code updated at Jan 04.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;


class test
{
    private static Dictionary<string, object> mainDict = new Dictionary<string, object>();

    public static void Main()
    {
        Dictionary<string, object> aSubDict = new Dictionary<string,object>();
        Dictionary<string, object> aSub1Dict = new Dictionary<string, object>();
        Dictionary<string, object> aSub2Dict = new Dictionary<string, object>();
        Dictionary<string, object> aSub3Dict = new Dictionary<string, object>();
        Dictionary<string, object> aSub4Dict = new Dictionary<string, object>();

        mainDict.Add("ADKey", aSubDict);
        mainDict.Add("ASKey", "AValue");
        aSubDict.Add("BDKey", aSub1Dict);
        aSubDict.Add("BSKey", "BValue");
        aSub1Dict.Add("CDKey", aSub2Dict);
        aSub1Dict.Add("CSKey", "CValue");
        aSub2Dict.Add("DDKey",aSub3Dict);
        aSub2Dict.Add("DSKey", "DValue");
        aSub3Dict.Add("EDKey", aSub4Dict);
        aSub3Dict.Add("ESKey", "EValue");
        aSub4Dict.Add("FKey", "FValue");


        StringBuilder sb;

        foreach (KeyValuePair<string, object> pair1 in mainDict)
            // watch out for NullReferenceException
            if (!ReferenceEquals(null, mainDict[pair1.Key]) && (mainDict[pair1.Key] is string))
            {
                Console.WriteLine("Key = {0}, Value = {1}", pair1.Key, pair1.Value);
                sb = new StringBuilder();
                sb.AppendFormat("{0}, {1}", pair1.Key, pair1.Value);
                Console.WriteLine(sb.ToString());
            }
            // IDictionary is not the one from the Generics namespace, it is the one from the System.Collections namespace
            else if (!ReferenceEquals(null, mainDict[pair1.Key]) && (mainDict[pair1.Key] is Dictionary<string, object>))
            {
                foreach (KeyValuePair<string, object> pair2 in (Dictionary<string, object>)pair1.Value)
                    if (!ReferenceEquals(null, ((Dictionary<string, object>)pair1.Value)[pair2.Key]) && (((Dictionary<string, object>)pair1.Value)[pair2.Key] is string))
                    {
                        Console.WriteLine("SubKey = {0}, Value = {1}", pair2.Key, pair2.Value);
                        sb = new StringBuilder();
                        sb.AppendFormat("{0}/{1}, {2}", pair1.Key, pair2.Key, pair2.Value);
                        Console.WriteLine(sb.ToString());
                    }
                    else if (!ReferenceEquals(null, ((Dictionary<string, object>)pair1.Value)[pair2.Key]) && (((Dictionary<string, object>)pair1.Value)[pair2.Key] is Dictionary<string, object>))
                    {
                        foreach (KeyValuePair<string, object> pair3 in (Dictionary<string, object>)pair2.Value)
                            if (!ReferenceEquals(null, ((Dictionary<string, object>)pair2.Value)[pair3.Key]) && (((Dictionary<string, object>)pair2.Value)[pair3.Key] is string))
                            {
                                Console.WriteLine("SubKey = {0}, Value = {1}", pair3.Key, pair3.Value);
                                sb = new StringBuilder();
                                sb.AppendFormat("{0}/{1}/{2}, {3}", pair1.Key, pair2.Key, pair3.Key, pair3.Value);
                                Console.WriteLine(sb.ToString());
                            }
                            else if (!ReferenceEquals(null, ((Dictionary<string, object>)pair2.Value)[pair3.Key]) && (((Dictionary<string, object>)pair2.Value)[pair3.Key] is Dictionary<string, object>))
                            {
                                foreach (KeyValuePair<string, object> pair4 in (Dictionary<string, object>)pair3.Value)
                                    if (!ReferenceEquals(null, ((Dictionary<string, object>)pair3.Value)[pair4.Key]) && (((Dictionary<string, object>)pair3.Value)[pair4.Key] is string))
                                    {
                                        Console.WriteLine("SubKey = {0}, Value = {1}", pair4.Key, pair4.Value);
                                        sb = new StringBuilder();
                                        sb.AppendFormat("{0}/{1}/{2}/{3}, {4}", pair1.Key, pair2.Key, pair3.Key, pair4.Key, pair4.Value);
                                        Console.WriteLine(sb.ToString());
                                    }
                                    else if (!ReferenceEquals(null, ((Dictionary<string, object>)pair3.Value)[pair4.Key]) && (((Dictionary<string, object>)pair3.Value)[pair4.Key] is Dictionary<string, object>))
                                    {
                                        foreach (KeyValuePair<string, object> pair5 in (Dictionary<string, object>)pair4.Value)
                                            if (!ReferenceEquals(null, ((Dictionary<string, object>)pair4.Value)[pair5.Key]) && (((Dictionary<string, object>)pair4.Value)[pair5.Key] is string))
                                            {
                                                Console.WriteLine("SubKey = {0}, Value = {1}", pair5.Key, pair5.Value);
                                                sb = new StringBuilder();
                                                sb.AppendFormat("{0}/{1}/{2}/{3}/{4}, {5}", pair1.Key, pair2.Key, pair3.Key, pair4.Key, pair5.Key, pair5.Value);
                                                Console.WriteLine(sb.ToString());
                                            }
                                            else if (!ReferenceEquals(null, ((Dictionary<string, object>)pair4.Value)[pair5.Key]) && (((Dictionary<string, object>)pair4.Value)[pair5.Key] is Dictionary<string, object>))
                                            {
                                                foreach (KeyValuePair<string, object> pair6 in (Dictionary<string, object>)pair5.Value)
                                                    if (!ReferenceEquals(null, ((Dictionary<string, object>)pair5.Value)[pair6.Key]) && (((Dictionary<string, object>)pair5.Value)[pair6.Key] is string))
                                                    {
                                                        Console.WriteLine("SubKey = {0}, Value = {1}", pair6.Key, pair6.Value);
                                                        sb = new StringBuilder();
                                                        sb.AppendFormat("{0}/{1}/{2}/{3}/{4}/{5}, {6}", pair1.Key, pair2.Key, pair3.Key, pair4.Key, pair5.Key, pair6.Key, pair6.Value);
                                                        Console.WriteLine(sb.ToString());
                                                    }
                                                    else if (!ReferenceEquals(null, ((Dictionary<string, object>)pair5.Value)[pair6.Key]) && (((Dictionary<string, object>)pair5.Value)[pair6.Key] is Dictionary<string, object>))
                                                    {
                                                        Console.WriteLine("sub Dict Found");
                                                    }
                                            }
                                    }
                            }
                    }
            }         
    }

}

Output like this

SubKey = FKey, Value = FValue
ADKey/BDKey/CDKey/DDKey/EDKey/FKey, FValue
SubKey = ESKey, Value = EValue
ADKey/BDKey/CDKey/DDKey/ESKey, EValue
SubKey = DSKey, Value = DValue
ADKey/BDKey/CDKey/DSKey, DValue
SubKey = CSKey, Value = CValue
ADKey/BDKey/CSKey, CValue
SubKey = BSKey, Value = BValue
ADKey/BSKey, BValue
Key = ASKey, Value = AValue
ASKey, AValue
Kopje answered 4/1, 2010 at 7:51 Comment(0)
S
23

You can set the Length to 0. In .NET 4.0 there's a Clear() method too. As the docs for Clear state:

Clear is a convenience method that is equivalent to setting the Length property of the current instance to 0 (zero).

So it's not a big deal :)

I would personally avoid doing this unless you really need to though - I'd normally just create a new StringBuilder. In my view that's simpler to understand - it makes it clearer that you really don't need anything from the previous object any more.

Do you have any particular reason to want to reuse the object? If it's for performance reasons, have you measured the performance and found this is a bottleneck? I suppose it could be significant if you have an instance with a very large capacity, and you want to avoid allocating it again... but that feels like a bit of an edge case to me.

(All of this applies for dictionaries as well, btw. I can't remember the last time I cleared a dictionary.)

Strategy answered 4/1, 2010 at 7:53 Comment(9)
Hi Jon, i attached my sample code. I think i can't create a lot of new StringBuilder objs....I don't know the exact number if the mainDict changed, my mainDict will changed dynamically...Kopje
Why do you think you can't create a lot of new StringBuilder objects? I'm not suggesting declaring more StringBuilder variables - but when you want a new StringBuilder, just use sb = new StringBuilder(). It's pretty hard to understand what your code is trying to do, I'm afraid...Strategy
The mainDict created from a parse result of XSD file, the XSD file maintained by other team. I need create another xsd2cs.cs file(string[] map and type[] map) at compiler time. That's why i need print out all the related elements which separated by '/' as a joined string. then another runtime program could validate the realated XML based on the .cs file.Kopje
Sorry, i confued just now. <b>sb = new StringBuilder()</b> means create new instance not new variables. thanks.Kopje
@Nano: Yes, that creates a new instance. Why would you want to declare a new variable?Strategy
Acturally, i only want to meet my requirement. I confued about the concepts of new object variable and new object instance just now. I think following your guide to create a new instance is enough for me. I updated the code above.Kopje
Hi @JonSkeet, at the tail end of your answer here you describe reusing a StringBuilder as an edge case. As a performance architect, I think this is a oversimplification of key performance problems. There are at least two classes of software performance: 1.) Anybody can code it because it's low-throughput. 2.) High throughput scenarios that always require diligence or you suffer the performance consequences. The idea of it being an "edge case" is a bit of a straw man. You have to identify whether your implementation is high throughput or not. If so, you should be coding defensively for perf.Burr
Here's a pattern I've begun using in order to work around some of these performance limitations. I use this for more than just StringBuilders. [ThreadStatic] static StringBuilder? _builder; static StringBuilder builder = _builder.InitializeIsNull(() => new StringBuilder(1000));Burr
@JeffFischer: I stand by my assertion that there's no point in worrying too much until you've actually got performance measurements and goals. Edge cases are still important, but that doesn't mean they're not edge cases. But I'm not going to discuss this further - SO comment threads are not for discussion, and you're commenting on a post that's over 13 years old...Strategy
B
2

I agree with Jon Skeet's technical explanation, but I felt it worthwhile, as a performance architect, to describe why this a relevant question and under which circumstances developers should consider employing such a tactic.

Many websites, or other types of applications, do not have to concern themselves with high performance. These are trivial in nature to develop because users are not stressing the .NET Runtime. However, if you know you are developing a high-throughput application or designing to future-proof your application for that likely outcome, things that are otherwise optional quickly become requirements.

i.e. Not everyone is developing a Netflix, but when you know you are, you should design (and test) with this in mind.

So, why bother doing this until you've measured and verified it's causing issues?

I do performance profiling for a living helping development teams overcome such obstacles. The answer is: Almost nobody is doing any measuring.

I can verify that StringBuilders show up in profile traces just as well as string allocations. In high performance scenarios this is definitely a valid concern. If you're going to rely on measuring, then you better actually measure.

If you are developing high throughput applications, do not get caught in a continuum fallacy that low-through application and high-through application design requirements are equivalent. They're not.

Here's a pattern I utilize in order to multi-thread string builders inside of a Singleton DI lifetime object with a method using the string builder.

[ThreadStatic]
static StringBuilder? _builder;
static StringBuilder builder => _builder.InitializeIsNull(() => new StringBuilder(1000));
/// <summary>
        /// Uses pass-by-reference semantics of class to initialize and set reference.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="initialized"></param>
        /// <param name="initialize"></param>
        /// <returns></returns>
        public static T InitializeIsNull<T>(this T? initialized, Func<T> initialize) where T : class
        {
            if (initialized == null)
            {
                initialized = initialize();
            }

            return initialized;
        }
Burr answered 2/4, 2023 at 18:51 Comment(0)
C
0

I think rather then reusing an exting StringBuilder object, you should create a new StringBuilder object.

Courtship answered 4/1, 2010 at 7:58 Comment(5)
Any reason why? I don't really see any advantage in creating a new instance to re-using the existing instance. It depends upon the context.Skellum
@Upul, for my case, i think i'd rather to re-using the existing instance. better?Kopje
@Jaco: If you don't want any information from the old StringBuilder, why reuse it? It adds logical complexity IMO when you really just want to say, "I don't need anything from the old one, give me a new one."Strategy
@Jon I don't see how going sb = new StringBuilder() is any less complex than sb.Clear()Skellum
First time I disagree with Skeet in my life. Odds are I'm wrong, but maybe one could save time by doing .Clear() instead of new Stringbuilder() in a method that should be optimized? Being Skeet he probably thought of this, so maybe if this is the case there are better ways to optimize it, or clear is slower than a new instance.Azotemia

© 2022 - 2024 — McMap. All rights reserved.