Let's say that you want to output or concat strings. Which of the following styles do you prefer?
var p = new { FirstName = "Bill", LastName = "Gates" };
Console.WriteLine("{0} {1}", p.FirstName, p.LastName);
Console.WriteLine(p.FirstName + " " + p.LastName);
Do you rather use format or do you simply concat strings? What is your favorite? Is one of these hurting your eyes?
Do you have any rational arguments to use one and not the other?
I'd go for the second one.
If you're dealing with something that needs to be easy to read (and this is most code), I'd stick with the operator overload version UNLESS:
Under at least two of these circumstances, I would use StringBuilder instead.
I think this depends heavily on how complex the output is. I tend to choose whichever scenario works best at the time.
Pick the right tool based on the job :D Whichever looks cleanest!
Nice one!
Just added
And it is even faster (I guess string.Concat is called in both examples, but the first one requires some sort of translation).
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)
callsstring.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
If you execute this, it will evaluate as follows:
tmp
here is not really a local variable, but it is a temporary for the JIT (it's pushed on the IL stack). If you push a string on the stack (such asldstr
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 because 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 useCultureInfo
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 usestring.Format
with an explicitCultureInfo
.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 detailled answer
If you don't know the exact size of the string beforehand, it's best to use a policy like this to overallocate 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 overallocate 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 overallocates the underlying buffer in powers of two.string.Format
usesStringBuilder
under the hood.This makes your decision a basic trade-off between overallocate-and-append(-multiple) (w/w.o. culture) or just allocate-and-append.
Try this code.
It's a slightly modified version of your code.
1. I removed Console.WriteLine as it's probably few orders of magnitude slower than what I'm trying to measure.
2. I'm starting the Stopwatch before the loop and stopping it right after, this way I'm not losing precision if the function takes for example 26.4 ticks to execute.
3. The way you divided the result by some iterations was wrong. See what happens if you have 1000 milliseconds and 100 milliseconds. In both situations, you will get 0 ms after dividing it by 1000000.
Those are my results:
Concatenating strings is fine in a simple scenario like that - it is more complicated with anything more complicated than that, even LastName, FirstName. With the format you can see, at a glance, what the final structure of the string will be when reading the code, with concatenation it becomes almost impossible to immediately discern the final result (except with a very simple example like this one).
What that means in the long run is that when you come back to make a change to your string format, you will either have the ability to pop in and make a few adjustments to the format string, or wrinkle your brow and start moving around all kinds of property accessors mixed with text, which is more likely to introduce problems.
If you're using .NET 3.5 you can use an extension method like this one and get an easy flowing, off the cuff syntax like this:
Finally, as your application grows in complexity you may decide that to sanely maintain strings in your application you want to move them into a resource file to localize or simply into a static helper. This will be MUCH easier to achieve if you have consistently used formats, and your code can be quite simply refactored to use something like