Since I don't think the answers here cover everything, I'd like to make a small addition here.
Console.WriteLine(string format, params object[] pars)
calls string.Format
. The '+' implies string concatenation. I don't think this always has to do with style; I tend to mix the two styles depending on the context I'm in.
Short answer
The decision you're facing has to do with string allocation. I'll try to make it simple.
Say you have
string s = a + "foo" + b;
If you execute this, it will evaluate as follows:
string tmp1 = a;
string tmp2 = "foo"
string tmp3 = concat(tmp1, tmp2);
string tmp4 = b;
string s = concat(tmp3, tmp4);
tmp
here is not really a local variable, but it is temporary for the JIT (it's pushed on the IL stack). If you push a string on the stack (such as ldstr
in IL for literals), you put a reference to a string pointer on the stack.
The moment you call concat
this reference becomes a problem because there isn't any string reference available that contains both strings. This means that .NET needs to allocate a new block of memory, and then fill it with the two strings. The reason this is a problem is that allocation is relatively expensive.
Which changes the question to: How can you reduce the number of concat
operations?
So, the rough answer is: string.Format
for >1 concats, '+' will work just fine for 1 concat. And if you don't care about doing micro-performance optimizations, string.Format
will work just fine in the general case.
A note about Culture
And then there's something called culture...
string.Format
enables you to use CultureInfo
in your formatting. A simple operator '+' uses the current culture.
This is especially an important remark if you're writing file formats and f.ex. double
values that you 'add' to a string. On different machines, you might end up with different strings if you don't use string.Format
with an explicit CultureInfo
.
F.ex. consider what happens if you change a '.' for a ',' while writing your comma-seperated-values file... in Dutch, the decimal separator is a comma, so your user might just get a 'funny' surprise.
More detailed answer
If you don't know the exact size of the string beforehand, it's best to use a policy like this to over allocate the buffers you use. The slack space is first filled, after which the data is copied in.
Growing means allocating a new block of memory and copying the old data to the new buffer. The old block of memory can then be released. You get the bottom line at this point: growing is an expensive operation.
The most practical way to do this is to use an overallocation policy. The most common policy is to over allocate buffers in powers of 2. Of course, you have to do it a bit smarter than that (since it makes no sense to grow from 1,2,4,8 if you already know you need 128 chars) but you get the picture. The policy ensures you don't need too many of the expensive operations I described above.
StringBuilder
is a class that basically over allocates the underlying buffer in powers of two. string.Format
uses StringBuilder
under the hood.
This makes your decision a basic trade-off between over-allocate-and-append (-multiple) (w/w.o. culture) or just allocate-and-append.
string.Format
that doesn't use any composite formatting features (i.e. just simple{0}
) and replace them with the considerably faster string concatenation. I wonder such a feat is achievable with an existing IL rewriter such as PostSharp. – Confabulate