To StringBuilder Or Not To StringBuilder

18. March 2008 06:14

Between work and stuff going on in my personal life, there hasn't been a lot of spare time for blogging and developer community things over the last few weeks.  Hopefully, that will be changing in the near future.  In the meantime, I wanted to squeeze in a quick blog post about something that came up today regarding StringBuilder.

It was pointed out someone on my team was concatenating several strings to build a query in a few methods.  It is arguable whether these particular queries should be embedded in the source code, but that isn't what this post is about.  It was requested that the developer modify the source code to use a StringBuilder rather than concatenating the strings.  Generally speaking, I would agree with this approach as the StringBuilder is more efficient when concatenating several strings, but this wasn't one of those cases.

Consider the following two methods:

public static string LiteralConcatenationExample()
{
    return "SELECT T1.Field1, " +
                  "T1.Field2, " +
                  "T1.Field3, " +
                  "T2.Field4 " +
            "FROM Table1 T1 " +
            "INNER JOIN Table2 T2 ON T1.Field1 = T2.Field1";
}

public static string StringBuilderWithLiteralsExample()
{
    StringBuilder sb = new StringBuilder();

    sb.Append("SELECT T1.Field1, ");
    sb.Append("T1.Field2, ");
    sb.Append("T1.Field3, ");
    sb.Append("T2.Field4 ");
    sb.Append("FROM Table1 T1 ");
    sb.Append("INNER JOIN Table2 T2 ON T1.Field1 = T2.Field1");

    return sb.ToString();
}

Which method do you think would be more efficient?

If you chose the second method, you should take a closer look at the generated IL:

.method public hidebysig static string  LiteralConcatenationExample() cil managed
{
  // Code size       11 (0xb)
  .maxstack  1
  .locals init ([0] string CS$1$0000)
  IL_0000:  nop
  IL_0001:  ldstr      "SELECT T1.Field1, T1.Field2, T1.Field3, T2.Field4 "
  + "FROM Table1 T1 INNER JOIN Table2 T2 ON T1.Field1 = T2.Field1"
  IL_0006:  stloc.0
  IL_0007:  br.s       IL_0009
  IL_0009:  ldloc.0
  IL_000a:  ret
} // end of method Program::LiteralConcatenationExample
.method public hidebysig static string  StringBuilderWithLiteralsExample() cil managed
{
  // Code size       90 (0x5a)
  .maxstack  2
  .locals init ([0] class [mscorlib]System.Text.StringBuilder sb,
           [1] string CS$1$0000)
  IL_0000:  nop
  IL_0001:  newobj     instance void [mscorlib]System.Text.StringBuilder::.ctor()
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  ldstr      "SELECT T1.Field1, "
  IL_000d:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
  IL_0012:  pop
  IL_0013:  ldloc.0
  IL_0014:  ldstr      "T1.Field2, "
  IL_0019:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
  IL_001e:  pop
  IL_001f:  ldloc.0
  IL_0020:  ldstr      "T1.Field3, "
  IL_0025:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
  IL_002a:  pop
  IL_002b:  ldloc.0
  IL_002c:  ldstr      "T2.Field4 "
  IL_0031:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
  IL_0036:  pop
  IL_0037:  ldloc.0
  IL_0038:  ldstr      "FROM Table1 T1 "
  IL_003d:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
  IL_0042:  pop
  IL_0043:  ldloc.0
  IL_0044:  ldstr      "INNER JOIN Table2 T2 ON T1.Field1 = T2.Field1"
  IL_0049:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
  IL_004e:  pop
  IL_004f:  ldloc.0
  IL_0050:  callvirt   instance string [mscorlib]System.Object::ToString()
  IL_0055:  stloc.1
  IL_0056:  br.s       IL_0058
  IL_0058:  ldloc.1
  IL_0059:  ret
} // end of method Program::StringBuilderWithLiteralsExample

As you can see, the string concatenation is considerable more efficient.  This may be a bit confusing to some people as you have undoubtedly had it hammered into your head that a StringBuilder should be used when concatenating strings.  Well, I would agree when you are dealing with dynamic strings that are built using parameters.  However, literal strings are a different story.

Since this scenario is using literal strings, the compiler can optimize the values into one large string that can be loaded with a single load string operation (ldstr).  If the code is modified to use a StringBuilder for each line of the query, it actually increases the number of operations by four for each line of the query.

In fairness, most situations will probably be dealing with dynamic strings, which would change this scenario back to favor the usage of a StringBuilder.  However, you should keep in mind that a StringBuilder doesn't automagically give you a more efficient solution in every situation, especially if you are only concatenating a few strings.

Comments

3/17/2008 10:34:06 PM #

http://

Interesting find!

http:// |

3/19/2008 7:19:42 AM #

http://

Very interesting!  Resembles our recent topic we had a little.

http:// |

4/14/2008 12:01:37 PM #

http://

I wouldn't use neither ways..

The code below is more readable than the first and has the same effects when translated to IL.

return @"SELECT
    T1.Field1,                  
    T1.Field2,
    T1.Field3,
    T2.Field4

            FROM Table1 T1

            INNER JOIN Table2 T2
    ON T1.Field1 = T2.Field1";

I also did some research and stringbuilder is no longer the best way to work.

You may want to perform some testing. So try this:
www.codeproject.com/.../...gBuilder_vs_String.aspx

It's nice anyway...

http:// |

4/15/2008 4:30:01 PM #

http://

Whether it is more readable is a question of semantics.  Regardless, I agree with you on the principle that StringBuilder would be unnecessary overhead in this situation.

http:// |

Comments are closed

About Me

I'm a passionate software developer and advocate of the Microsoft .NET platform.  In my opinion, software development is a craft that necessitates a conscious effort to continually improve your skills rather than falling into the trap of complacency.  I was also awarded as a Microsoft MVP in Connected Systems in 2008, 2009, and 2010.


Can’t code withoutThe best C# & VB.NET refactoring plugin for Visual Studio
Follow jeff_barnes on Twitter

View Jeff Barnes's profile on LinkedIn

 

Shared Items

Disclaimer

Anything you read or see on this site is solely based on my own thoughts.  The material on this site does not necessarily reflect the views of my employer or anyone else.  In other words, I don't speak for anyone other than myself.  So, don't assume I am the official spokesperson for anyone.