So the other day I was doing a code review for a fellow work colleague and I noticed more and more people on my team are using String.Format("{0} {1} {2}) vs. string + string + string when concatenating strings. Our coding standards don't address this particular issue. I have read up on the differences and pros and cons many times. This time, I decided to undertake my own tests.
I created a simple application for my purposes and then looked at the IL (Intermediate Language) through ILDASM to see what's going on under the hood. This article is not meant to discuss all possibilities such as using the Sytem.Text.StringBuilder.
String Interning
Before you read any further, you must understand a little about the CLR and string manipulation. To be as brief as possible, every time your code creates a string, the CLR may add an entry to what's called the intern pool. It's called String Interning.
The common language runtime (CLR) conserves string storage by maintaining a table, called the intern pool, which contains a single reference to each unique literal string declared or created programmatically in your program. Consequently, an instance of a literal string with a particular value only exists once in the system.
What this means is if you have code like this:
string one = "Shane";
string two = "Shane";
There will only be a single instance or occurrence of "Shane" in the intern pool.
Format vs. +
Back the subject matter.
Let's compare the two lines of code that concatenate strings. They both concatenate a string.
// Declare vars
string empty1 = "";
string empty2 = string.Empty;
string something1 = "Shane Weebe";
string something2 = "Landon Donovan";
// Concact strings
string concact1 = "something2: " + something2 + ", something1: " + something1 + ", empty2:" + empty2 + "empty1: " + empty1;
string concact2 = string.Format("empty1: {0}, empty2: {1}, something1 {2}, something2 {3}", empty1, empty2, something1, something2);
Both lines of code accomplish the same task at the end of the day: concat1 and concat2 will contain the same string. They both concatenate a string. Looking at the IL (intermediate language), which is what the .NET compiler creates, here is what we see:
Concact1 (I removed some lines for easier viewing). The orange lines are constructed from the hard coded strings in the c# code; the blue lines are constructed from the variables.
IL_001a: newarr [mscorlib]System.String
IL_0021: ldloc.s CS$0$0000
IL_0023: ldc.i4.0
IL_0024: ldstr "something2: "
IL_002a: ldloc.s CS$0$0000
IL_002c: ldc.i4.1
IL_002f: ldloc.s CS$0$0000
IL_0031: ldc.i4.2
IL_0032: ldstr ", something1: "
IL_0038: ldloc.s CS$0$0000
IL_003a: ldc.i4.3
IL_003d: ldloc.s CS$0$0000
IL_003f: ldc.i4.4
IL_0040: ldstr ", empty2:"
IL_0046: ldloc.s CS$0$0000
IL_0048: ldc.i4.5
IL_004b: ldloc.s CS$0$0000
IL_004d: ldc.i4.6
IL_004e: ldstr "empty1: "
IL_0054: ldloc.s CS$0$0000
IL_0056: ldc.i4.7
IL_0059: ldloc.s CS$0$0000
IL_005b: call string [mscorlib]System.String::Concat(string[])
IL_0060: stloc.s concact1
IL_0062: ldstr "empty1: {0}, empty2: {1}, something1 {2}, something2 {3}"
Concact2 (I removed some lines for easier reading). Notice only blue lines from the variables.
IL_0068: newarr [mscorlib]System.Object
IL_006d: stloc.s CS$0$0001
IL_006f: ldloc.s CS$0$0001
IL_0071: ldc.i4.0
IL_0074: ldloc.s CS$0$0001
IL_0076: ldc.i4.1
IL_0079: ldloc.s CS$0$0001
IL_007b: ldc.i4.2
IL_007e: ldloc.s CS$0$0001
IL_0080: ldc.i4.3
IL_0083: ldloc.s CS$0$0001
IL_0085: call string [mscorlib]System.String::Format(string, object[])
IL_008a: stloc.s concact2
If you look at concatenating using the + operator (concat1), the compiler creates a new string array, fills the array with the various variables and hard coded text, then calls String.Concat to combine them, nearly replicating the c# code from concat2.
If you look at the String.Format method (concat2), the compiler creates a new object array, fills the array with the various strings and then calls String.Format method to process the concatenation.
Summary
Looking at the IL the String.Format method appears to be a more optimized approach to string concatenation as the IL doesn't have to combine the hardcoded text and variables in a String array, nor does it have to call String.Concat.
However, let's not forget about the intern pool mentioned above. While the compiler creates the IL, the CLR will still use the intern pool to manage string resources in memory. Since we had declared the strings with default values, those values will be in the intern pool in both concat1 and concat2. This means, that even though the IL created a string array for the Concat1 and, consumed a few more lines of IL code, in terms of memory there is no additional overheard.
My conclusion is use what you feel is more readable. I think String.Format is smarter when you are combining many strings, but the + operator is easier and more readable for simple concatenations.