Am I undermining the effectiveness of StringBuilder?

I started using StringBuilder instead of direct concatenation, but it looks like it lacks a decisive method. Therefore, I myself implemented this as an extension:

 public void Append(this StringBuilder stringBuilder, params string[] args) { foreach (string arg in args) stringBuilder.Append(arg); } 

This turns the following mess:

 StringBuilder sb = new StringBuilder(); ... sb.Append(SettingNode); sb.Append(KeyAttribute); sb.Append(setting.Name); 

In it:

 sb.Append(SettingNode, KeyAttribute, setting.Name); 

I could use sb.AppendFormat("{0}{1}{2}",... but that seems even less preferable and even harder to read. Is my extension a good method or does it somehow undermine the advantages of StringBuilder ? I am not trying to prematurely optimize anything, since my method is more readable than speed, but I would also like to know that I am not shooting my own leg.

+52
stringbuilder c #
Aug 13 '10 at 19:40
source share
10 answers

I do not see a problem with your extension. If this works for you, all is well.

I myself prefer:

 sb.Append(SettingNode) .Append(KeyAttribute) .Append(setting.Name); 
+69
Aug 13 '10 at 19:45
source share

Such questions can always be answered with a simple test case.

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; namespace SBTest { class Program { private const int ITERATIONS = 1000000; private static void Main(string[] args) { Test1(); Test2(); Test3(); } private static void Test1() { var sw = Stopwatch.StartNew(); var sb = new StringBuilder(); for (var i = 0; i < ITERATIONS; i++) { sb.Append("TEST" + i.ToString("00000"), "TEST" + (i + 1).ToString("00000"), "TEST" + (i + 2).ToString("00000")); } sw.Stop(); Console.WriteLine("Testing Append() extension method..."); Console.WriteLine("--------------------------------------------"); Console.WriteLine("Test 1 iterations: {0:n0}", ITERATIONS); Console.WriteLine("Test 1 milliseconds: {0:n0}", sw.ElapsedMilliseconds); Console.WriteLine("Test 1 output length: {0:n0}", sb.Length); Console.WriteLine(""); } private static void Test2() { var sw = Stopwatch.StartNew(); var sb = new StringBuilder(); for (var i = 0; i < ITERATIONS; i++) { sb.Append("TEST" + i.ToString("00000")); sb.Append("TEST" + (i+1).ToString("00000")); sb.Append("TEST" + (i+2).ToString("00000")); } sw.Stop(); Console.WriteLine("Testing multiple calls to Append() built-in method..."); Console.WriteLine("--------------------------------------------"); Console.WriteLine("Test 2 iterations: {0:n0}", ITERATIONS); Console.WriteLine("Test 2 milliseconds: {0:n0}", sw.ElapsedMilliseconds); Console.WriteLine("Test 2 output length: {0:n0}", sb.Length); Console.WriteLine(""); } private static void Test3() { var sw = Stopwatch.StartNew(); var sb = new StringBuilder(); for (var i = 0; i < ITERATIONS; i++) { sb.AppendFormat("{0}{1}{2}", "TEST" + i.ToString("00000"), "TEST" + (i + 1).ToString("00000"), "TEST" + (i + 2).ToString("00000")); } sw.Stop(); Console.WriteLine("Testing AppendFormat() built-in method..."); Console.WriteLine("--------------------------------------------"); Console.WriteLine("Test 3 iterations: {0:n0}", ITERATIONS); Console.WriteLine("Test 3 milliseconds: {0:n0}", sw.ElapsedMilliseconds); Console.WriteLine("Test 3 output length: {0:n0}", sb.Length); Console.WriteLine(""); } } public static class SBExtentions { public static void Append(this StringBuilder sb, params string[] args) { foreach (var arg in args) sb.Append(arg); } } } 

On my pc, the output is:

 Testing Append() extension method... -------------------------------------------- Test 1 iterations: 1,000,000 Test 1 milliseconds: 1,080 Test 1 output length: 29,700,006 Testing multiple calls to Append() built-in method... -------------------------------------------- Test 2 iterations: 1,000,000 Test 2 milliseconds: 1,001 Test 2 output length: 29,700,006 Testing AppendFormat() built-in method... -------------------------------------------- Test 3 iterations: 1,000,000 Test 3 milliseconds: 1,124 Test 3 output length: 29,700,006 

So your extension method is only slightly slower than the Append () method, and slightly faster than the AppendFormat () method, but in all three cases the difference is too trivial to worry about. Thus, if your extension method improves the readability of your code, use it!

+32
Aug 13 '10 at 20:01
source share

This is a little overhead creating an extra array, but I doubt it is a lot. You have to measure

If it turns out that the overhead of creating arrays of strings is significant, you can reduce it by having several overloads - one for two parameters, one for three, one for four, etc ... so that only if you achieve more the number of parameters (for example, six or seven) will need to create an array. Overloads will be as follows:

 public void Append(this builder, string item1, string item2) { builder.Append(item1); builder.Append(item2); } public void Append(this builder, string item1, string item2, string item3) { builder.Append(item1); builder.Append(item2); builder.Append(item3); } public void Append(this builder, string item1, string item2, string item3, string item4) { builder.Append(item1); builder.Append(item2); builder.Append(item3); builder.Append(item4); } // etc 

And then one final overload using params like

 public void Append(this builder, string item1, string item2, string item3, string item4, params string[] otherItems) { builder.Append(item1); builder.Append(item2); builder.Append(item3); builder.Append(item4); foreach (string item in otherItems) { builder.Append(item); } } 

I would, of course, expect these (or just your original extension method) to be faster than using AppendFormat - for this you need to AppendFormat format string.

Note that I did not do these overloads with each other pseudo-recursively - I suspect that they were built-in, but if they were not the overhead of creating a new stack frame, etc., this could become significant. (We assume that the overhead of the array is significant if we have it this far.)

+9
Aug 13 '10 at 19:49
source share

Besides a bit of overhead, I personally don't see any problems with it. Definitely more readable. As long as you go through a reasonable amount of parameters, I don't see a problem.

+3
Aug 13 '10 at 19:44
source share

In terms of clarity, your extension is fine.

It would probably be better to just use the .append (x) .append (y) .append (z) format if you have never had more than 5 or 6 elements.

StringBuilder alone will provide you with a performance boost if you process many thousands of elements. In addition, you will create an array every time you call a method.

So, if you do this for clarity, that's fine. If you do this for efficiency, then you are likely to make a mistake.

+2
Aug 13 '10 at 20:08
source share

I would not say that you undermine its effectiveness, but you can do something inefficient when a more effective method is available. AppendFormat is what I think you want here. If the string {0} {1} {2} is used all the time, this is too ugly, I usually put my formatting strings in the constants above, so the look will be more or less the same as your extension.

 sb.AppendFormat(SETTING_FORMAT, var1, var2, var3); 
+1
Aug 13 '10 at 19:45
source share

I have not tested recently, but in the past, StringBuilder was actually slower than just string concatenation ("this" + "that") until you reach about 7 concatenations.

If this is a string concatenation that doesn't run in a loop, you might need to consider whether to use StringBuilder at all. (In the loop, I start to worry about distribution by simply concatenating strings, since strings are immutable.)

+1
Aug 14 '10 at 1:12
source share

Potentially even faster, because it performs no more than one redistribution / copy step for many applications.

 public void Append(this StringBuilder stringBuilder, params string[] args) { int required = stringBuilder.Length; foreach (string arg in args) required += arg.Length; if (stringBuilder.Capacity < required) stringBuilder.Capacity = required; foreach (string arg in args) stringBuilder.Append(arg); } 
+1
Aug 12 '14 at
source share

Ultimately, it comes down to leading to less string creation. I have a feeling that the extension will result in a higher number of strings using the string format. But performance will probably not be that different.

0
Aug 13 '10 at 19:48
source share

Chris

Inspired by this answer by John Skeet (second answer), I rewrote your code a bit. Basically, I added the TestRunner method, which runs the passed function and reports the elapsed time, eliminating a little redundant code. Not to be smug, but rather as a programming exercise for yourself. Hope this will be helpful.

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; namespace SBTest { class Program { private static void Main(string[] args) { // JIT everything AppendTest(1); AppendFormatTest(1); int iterations = 1000000; // Run Tests TestRunner(AppendTest, iterations); TestRunner(AppendFormatTest, iterations); Console.ReadLine(); } private static void TestRunner(Func<int, long> action, int iterations) { GC.Collect(); var sw = Stopwatch.StartNew(); long length = action(iterations); sw.Stop(); Console.WriteLine("--------------------- {0} -----------------------", action.Method.Name); Console.WriteLine("iterations: {0:n0}", iterations); Console.WriteLine("milliseconds: {0:n0}", sw.ElapsedMilliseconds); Console.WriteLine("output length: {0:n0}", length); Console.WriteLine(""); } private static long AppendTest(int iterations) { var sb = new StringBuilder(); for (var i = 0; i < iterations; i++) { sb.Append("TEST" + i.ToString("00000"), "TEST" + (i + 1).ToString("00000"), "TEST" + (i + 2).ToString("00000")); } return sb.Length; } private static long AppendFormatTest(int iterations) { var sb = new StringBuilder(); for (var i = 0; i < iterations; i++) { sb.AppendFormat("{0}{1}{2}", "TEST" + i.ToString("00000"), "TEST" + (i + 1).ToString("00000"), "TEST" + (i + 2).ToString("00000")); } return sb.Length; } } public static class SBExtentions { public static void Append(this StringBuilder sb, params string[] args) { foreach (var arg in args) sb.Append(arg); } } } 

Here's the conclusion:

 --------------------- AppendTest ----------------------- iterations: 1,000,000 milliseconds: 1,274 output length: 29,700,006 --------------------- AppendFormatTest ----------------------- iterations: 1,000,000 milliseconds: 1,381 output length: 29,700,006 
0
Aug 18 2018-10-18
source share



All Articles