126

I've got a small piece of code that is parsing an index value to determine a cell input into Excel. It's got me thinking...

What's the difference between

xlsSheet.Write("C" + rowIndex.ToString(), null, title);

and

xlsSheet.Write(string.Format("C{0}", rowIndex), null, title);

Is one "better" than the other? And why?

Jay Bazuzi
  • 45,157
  • 15
  • 111
  • 168
Gavin Miller
  • 43,168
  • 21
  • 122
  • 188
  • 4
    This is similar to http://stackoverflow.com/questions/16432/c-string-output-format-or-concat – Jonathan S. Nov 17 '08 at 23:06
  • possible duplicate of [Why use String.Format?](http://stackoverflow.com/questions/4671610/why-use-string-format) – Druid Aug 02 '12 at 07:13

15 Answers15

163

My initial preference (coming from a C++ background) was for String.Format. I dropped this later on due to the following reasons:

  • String concatenation is arguably "safer". It happened to me (and I've seen it happen to several other developers) to remove a parameter, or mess up the parameter order by mistake. The compiler will not check the parameters against the format string and you end up with a runtime error (that is, if you're lucky enough not to have it in an obscure method, such as logging an error). With concatenation, removing a parameter is less error prone. You could argue the chance of error is very small, but it may happen.

- String concatenation allows for null values, String.Format does not. Writing "s1 + null + s2" does not break, it just treats the null value as String.Empty. Well, this may depend on your specific scenario - there are cases where you'd like an error instead of silently ignoring a null FirstName. However even in this situation I personally prefer checking for nulls myself and throwing specific errors instead of the standard ArgumentNullException I get from String.Format.

  • String concatenation performs better. Some of the posts above already mention this (without actually explaining why, which determined me to write this post :).

Idea is the .NET compiler is smart enough to convert this piece of code:

public static string Test(string s1, int i2, int i3, int i4, 
        string s5, string s6, float f7, float f8)
{
    return s1 + " " + i2 + i3 + i4 + " ddd " + s5 + s6 + f7 + f8;
}

to this:

public static string Test(string s1, int i2, int i3, int i4,
            string s5, string s6, float f7, float f8)
{
    return string.Concat(new object[] { s1, " ", i2, i3, i4, 
                    " ddd ", s5, s6, f7, f8 });
}

What happens under the hood of String.Concat is easy to guess (use Reflector). The objects in the array get converted to their string via ToString(). Then the total length is computed and only one string allocated (with the total length). Finally, each string is copied into the resulting string via wstrcpy in some unsafe piece of code.

Reasons String.Concat is way faster? Well, we can all have a look what String.Format is doing - you'll be surprised at the amount of code required to process the format string. On top of this (I've seen comments regarding the memory consumption), String.Format uses a StringBuilder internally. Here's how:

StringBuilder builder = new StringBuilder(format.Length + (args.Length * 8));

So for every passed argument, it reserves 8 characters. If the argument is a one-digit value, then too bad, we have some wasted space. If the argument is a custom object returning some long text on ToString(), then there might be even some reallocation needed (worst-case scenario, of course).

Compared to this, the concatenation only wastes the space of the object array (not too much, taking into account it's an array of references). There's no parsing for format specifiers and no intermediary StringBuilder. The boxing/unboxing overhead is present in both methods.

The only reason I'd go for String.Format is when localization is involved. Putting format strings in resources allows you to support different languages without messing with the code (think about scenarios where formatted values change order depending on the language, i.e. "after {0} hours and {1} minutes" may look quite different in Japanese :).


To sum up my first (and quite long) post:

  • best way (in terms of performance vs. maintainability/readability) for me is using string concatenation, without any ToString() calls
  • if you're after performance, make the ToString() calls yourself to avoid boxing (I'm somewhat biased towards readability) - same as first option in your question
  • if you're showing localized strings to the user (not the case here), String.Format() has an edge.
Dan C.
  • 3,643
  • 2
  • 21
  • 19
  • 5
    1) `string.Format` is "safe" when using ReSharper; that is, it is as safe as any other code that can be used [incorrectly]. 2) `string.Format` *does* allow for a "safe" `null`: `string.Format("A{0}B", (string)null)` results in "AB". 3) I seldom care about this level of performance (and to that end, it is a *rare day* when I pull out `StringBuilder`)... –  Mar 19 '12 at 22:46
  • Agree on 2), I will edit the post. Can't verify if this was safe back in 1.1, but the latest framework is indeed null-safe. – Dan C. Mar 20 '12 at 09:27
  • Is string.Concat still used if one of the operands is a method call with a return value, rather than being a parameter or variable? – Richard Collette Jan 14 '13 at 15:26
  • 2
    @RichardCollette Yes, String.Concat is used even of you concatenate return values of method calls, e.g. `string s = "This " + MyMethod(arg) + " is a test";` gets compiled to a `String.Concat()` call in Release mode. – Dan C. Jan 22 '13 at 18:29
119

Before C# 6

To be honest, I think the first version is simpler - although I'd simplify it to:

xlsSheet.Write("C" + rowIndex, null, title);

I suspect other answers may talk about the performance hit, but to be honest it'll be minimal if present at all - and this concatenation version doesn't need to parse the format string.

Format strings are great for purposes of localisation etc, but in a case like this concatenation is simpler and works just as well.

With C# 6

String interpolation makes a lot of things simpler to read in C# 6. In this case, your second code becomes:

xlsSheet.Write($"C{rowIndex}", null, title);

which is probably the best option, IMO.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    I know I know. It was made in jest (have read the link btw before, which was a good read) – nawfal May 12 '13 at 11:24
  • Jon. I've always been a fan of Mr. Richter, and have followed guidance on boxing etc. religiously. However, after reading your (old) article, I am now a convert. Thanks – stevethethread Jan 21 '14 at 14:42
  • 4
    @mbomb007: It's now at http://codeblog.jonskeet.uk/2008/10/08/why-boxing-doesn-t-keep-me-awake-at-nights/ – Jon Skeet May 14 '15 at 19:57
  • 4
    Now that C# 6 is available, you can use the new string interpolation syntax for what I think is even easier readability: `xlsSheet.Write($"C{rowIndex}", null, title);` – HotN Jul 24 '15 at 14:54
  • @JonSkeet: [that link](http://codeblog.jonskeet.uk/2008/10/08/why-boxing-doesn-t-keep-me-awake-at-nights/) is also dead – Tim Schmelter Sep 05 '16 at 13:17
  • @TimSchmelter: Hmm... not for me... checked just now. What error do you get? – Jon Skeet Sep 05 '16 at 13:53
  • @JonSkeet: in chrome: ERR_NAME_RESOLUTION_FAILED, in IE too. But `http://codeblog.jonskeet.uk/` is also inaccessible – Tim Schmelter Sep 05 '16 at 13:55
  • @TimSchmelter: Okay, then that sounds like the problem... no idea why that would be, but I'm just leaving work so will try on mobile and at home... – Jon Skeet Sep 05 '16 at 14:07
  • @JonSkeet - Your profile image sounds familiar.. isn't it "Give up and go SO and let Jon Skeet rescue the princess". BTW that was the best way to rescue the princess.. I am one of your biggest fan, several times you rescued princess for me .. :D –  Aug 18 '17 at 08:04
  • So now with c# 6 you would never use string concatenation anymore? always string interpolation? no use cases for string concatenation? – BornToCode May 31 '18 at 07:01
  • @BornToCode: I wouldn't say *never* - but I'd *usually* use string interpolation. – Jon Skeet May 31 '18 at 09:45
  • I think that @P Daddy created a nice heuristic for this in his answer "unless I'm just concatenating a value directly to the beginning or end of a string literal, I almost always use string interpolation", so following that rule I find that the specific example you quoted `"C" + rowIndex` is actually more readable than $"C{rowIndex}". But that could only be my opinion.. It's hard to contradict the great Skeet ;) – BornToCode May 31 '18 at 16:57
6

I think the first option is more readable and that should be your primary concern.

xlsSheet.Write("C" + rowIndex.ToString(), null, title);

string.Format uses a StringBuilder under the hood (check with reflector) so it won't have any performance benefit unless you are doing a significant amount of concatenation. It will be slower for your scenario but the reality is this micro performance optimisation decision is inappropriate most of the time and you should really be focusing on the readability of your code unless you are in a loop.

Either way, write for readability first and then use a performance profiler to identify your hotspots if you really think you have performance concerns.

Martin Hollingsworth
  • 7,249
  • 7
  • 49
  • 51
  • Reflector: http://www.red-gate.com/products/reflector/ ANTS Profiler: http://www.red-gate.com/products/ants_profiler/index.htm – Jason Slocomb Nov 17 '08 at 23:01
5

For a simple case where it's a simple single concatenation, I feel that it's not worth the complexity of string.Format (and I haven't tested, but I suspect that for a simple case like this, string.Format might be slightly slower, what with the format string parsing and all). Like Jon Skeet, I prefer to not explicitly call .ToString(), since that will be done implicitly by the string.Concat(string, object) overload, and I think the code is cleaner-looking and easier to read without it.

But for more than a few concatenations (how many is subjective), I definitely prefer string.Format. At a certain point I think that both readability and performance suffer unnecessarily with concatenation.

If there are many parameters to the format string (again, "many" is subjective), I usually prefer to include commented indices on the replacement arguments, lest I lose track of which value goes to which parameter. A contrived example:

Console.WriteLine(
    "Dear {0} {1},\n\n" +

    "Our records indicate that your {2}, \"{3}\", is due for {4} {5} shots.\n" +
    "Please call our office at 1-900-382-5633 to make an appointment.\n\n" +

    "Thank you,\n" +
    "Eastern Veterinary",

    /*0*/client.Title,
    /*1*/client.LastName,
    /*2*/client.Pet.Animal,
    /*3*/client.Pet.Name,
    /*4*/client.Pet.Gender == Gender.Male ? "his" : "her",
    /*5*/client.Pet.Schedule[0]
);

Update

It occurs to me that the example I've given is a bit confusing, because it appears that I've used both concatenation and string.Format here. And yes, logically and lexically, that's what I've done. But the concatenations will all be optimized away by the compiler1, since they're all string literals. So at run-time, there will be a single string. So I guess I should say that I prefer to avoid many concatenations at run time.

Of course, most of this topic is out of date now, unless you're still stuck using C# 5 or older. Now we have interpolated strings, which for readability, are far superior to string.Format, in almost all cases. These days, unless I'm just concatenating a value directly to the beginning or end of a string literal, I almost always use string interpolation. Today, I'd write my earlier example like this:

Console.WriteLine(
    $"Dear {client.Title} {client.LastName},\n\n" +

    $"Our records indicate that your {client.Pet.Animal}, \"{client.Pet.Name}\", " +
    $"is due for {(client.Pet.Gender == Gender.Male ? "his" : "her")} " +
    $"{client.Pet.Schedule[0]} shots.\n" +
    "Please call our office at 1-900-382-5633 to make an appointment.\n\n" +

    "Thank you,\n" +
    "Eastern Veterinary"
);

You do lose compile-time concatenation this way. Each interpolated string gets turned into a call to string.Format by the compiler, and their results are concatenated at run time. That means this is a sacrifice of run-time performance for readability. Most of the time, it's a worthwhile sacrifice, because the run-time penalty is negligible. In performance critical code, however, you may need to profile different solutions.


1 You can see this in the C# specification:

... the following constructs are permitted in constant expressions:

...

  • The predefined + ... binary operator...

You can also verify it with a little code:

const string s =
    "This compiles successfully, " +
    "and you can see that it will " +
    "all be one string (named `s`) " +
    "at run time";
P Daddy
  • 28,912
  • 9
  • 68
  • 92
3

If your string was more complex with many variables being concatenated, then I would choose the string.Format(). But for the size of string and number of variables being concatenated in your case, I'd go with your first version, it's more spartan.

Aaron Palmer
  • 8,912
  • 9
  • 48
  • 77
3

That example is probably too trivial to notice a difference. In fact, I think in most cases the compiler can optimize away any difference at all.

However, if I had to guess I'd give string.Format() an edge for more complicated scenarios. But that's more of a gut feeling that's it's likely to do a better job utilizing a buffer instead of producing multiple immutable strings, and not based on any real data.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
3

I have taken a look at String.Format (using Reflector) and it actually creates a StringBuilder then calls AppendFormat on it. So it is quicker than concat for multiple stirngs. Quickest (I believe) would be creating a StringBuilder and doing the calls to Append manually. Of course the number of "many" is up for guessing. I would use + (actually & becuase I am a VB programmer mostly) for something as simple as your example. As it gets more complex I use String.Format. If there are LOTS of variables then I would go for a StringBuilder and Append, for example, we have code that builds code, there I use one line of actual code to output one line of generated code.

There seems to be some speculation about how many strings get created for each of these operations, so lets take a few simple examples.

"C" + rowIndex.ToString();

"C" is already a string.
rowIndex.ToString() creates another string. (@manohard - no boxing of rowIndex will occur)
Then we get the final string.
If we take the example of

String.Format("C(0)",rowIndex);

then we have "C{0}" as a string
rowIndex gets boxed to be passed to the function
A new stringbuilder is created
AppendFormat is called on the string builder - I don't know the details of how AppendFormat functions but lets assume it is ultra efficient, it is still going to have to convert the boxed rowIndex into a string.
Then convert the stringbuilder into a new string.
I know that StringBuilders attempt to prevent pointless memory copies from taking place but the String.Format still ends up with extra overhead compared to the plain concatenation.

If we now take an example with a few more strings

"a" + rowIndex.ToString() + "b" + colIndex.ToString() + "c" + zIndex.ToString();

we have 6 strings to start with, which will be the same for all cases.
Using concatenation we also have 4 intermediate strings plus the final result. It is those intermediate results that are eliminated by using String,Format (or a StringBuilder).
Remember that to create each intermediate string, the previous one must be copied to a new memory location, it is not just the memory allocation that is potentially slow.

pipTheGeek
  • 2,703
  • 17
  • 16
  • 4
    Nitpick. In the "a" + ... + "b" + ... + "c" + ..., you won't actually have 4 intermediate strings. The compiler will generate a call to the String.Concat(params string[] values) static method, and they'll all be concatenated at once. I'd still prefer string.Format for readability's sake, though. – P Daddy Nov 19 '08 at 01:08
2

I like String.Format because can make your formatted text much easier to follow and read than inline concatenation, also its much more flexible allowing you to format your parameters, however for short uses like yours I see no problem about concatenating.

For concatenations inside loops or in big strings you should always try to use the StringBuilder class.

Christian C. Salvadó
  • 807,428
  • 183
  • 922
  • 838
2

About performance:

void Main()
{
    var start = CurrentTimeMillis();
    for (var i = 0; i < 1000000; i++)
    {
        var s = "Hi " + i.ToString() + "; Hi to you " + (i * 2).ToString();
    }
    var end = CurrentTimeMillis();
    Console.WriteLine("Concatenation = " + ((end - start)).ToString() + " millisecond");
    start = CurrentTimeMillis();
    for (var i = 0; i < 1000000; i++)
    {
        var s = String.Format("Hi {0}; Hi to you {1}", i, +i * 2);
    }
    end = CurrentTimeMillis();
    Console.WriteLine("Format = " + ((end - start)).ToString() + " millisecond");
    start = CurrentTimeMillis();
    for (var i = 0; i < 1000000; i++)
    {
        var s = String.Concat("Hi ", i.ToString(), "; Hi to you ", (i * 2).ToString());
    }
    end = CurrentTimeMillis();
    Console.WriteLine("Strng.concat = " + ((end - start)).ToString() + " millisecond");

    start = CurrentTimeMillis();
    for (int i = 0; i < 1000000; i++)
    {
        StringBuilder bldString = new StringBuilder("Hi ");
        bldString.Append(i).Append("; Hi to you ").Append(i * 2);
    }
    end = CurrentTimeMillis();
    Console.WriteLine("String Builder = " + ((end - start)) + " millisecond");
}

private static readonly DateTime Jan1st1970 = new DateTime
    (1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

public static long CurrentTimeMillis()
{
    return (long)(DateTime.UtcNow - Jan1st1970).TotalMilliseconds;
}

Result

Concatenation = 69 millisecond
Format = 142 millisecond
Strng.concat = 62 millisecond
String Builder = 91 millisecond

To make this a fair comparison, I instantiate a new StringBuilder rather than one created outside the loop (which is likely to be faster due to the looping append at the end of one builder's re-allocation space).

Nisar
  • 5,708
  • 17
  • 68
  • 83
  • I ran this a few tens of times with C# fiddle using the .NET6 compiler, and adding another loop that uses the new string interpolation `var s = $"Hi {i}; Hi to you {+i * 2}";` unsurprisingly the results of every test were slightly different to this answer on average, but string interpolation was approximately identical to StringBuilder. (Given other answers, they probably get converted to the same code by the compiler) – TheAtomicOption Apr 20 '22 at 17:01
1

I agree with alot of points above, another point that I believe should be mentioned is code maintainability. string.Format allows for easier changing of code.

i.e. I have a message "The user is not authorized for location " + location or "The User is not authorized for location {0}"

if I ever wanted to change the message to say: location + " does not allow this User Access" or "{0} does not allow this User Access"

with string.Format all I have to do is change the string. for concatenation I have to modify that message

if used in multiple places can save allot of time.

deankarn
  • 462
  • 2
  • 6
  • 17
1

I was under the impression that string.format was faster it seems to be 3 x slower in this test

string concat = "";
        System.Diagnostics.Stopwatch sw1 = new System.Diagnostics.Stopwatch    ();
        sw1.Start();
        for (int i = 0; i < 10000000; i++)
        {
            concat = string.Format("{0}{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}","1", "2" , "3" , "4" , "5" , "6" , "7" , "8" , "9" , "10" , i);
        }
        sw1.Stop();
        Response.Write("format: "  + sw1.ElapsedMilliseconds.ToString());
        System.Diagnostics.Stopwatch sw2 = new System.Diagnostics.Stopwatch();
        sw2.Start();
        for (int i = 0; i < 10000000; i++)
        {
            concat = "1" + "2" + "3" + "4" + "5" + "6" + "7" + "8" + "9" + "10" + i;
        }
        sw2.Stop();

string.format took 4.6 sec and when using '+' it took 1.6 secs.

Kitemark76
  • 101
  • 1
  • 8
  • 7
    The compiler recognizes `"1" + "2" + "3" + "4" + "5" + "6" + "7" + "8" + "9" + "10"` as one string literal, so the line effectively becomes `"12345678910" + i` which is faster the the previous `string.Format(...)` – wertzui Sep 23 '15 at 07:44
0

I prefer String.Format regarding to performance

Farzad J
  • 1,089
  • 2
  • 14
  • 28
0

string.Format is probably a better choice when the format template ("C{0}") is stored in a configuration file (such as Web.config / App.config)

Andrei Rînea
  • 20,288
  • 17
  • 117
  • 166
0

I did a bit of profiling of various string methods including string.Format, StringBuilder and string concatenation. String concatenation almost always outperformed the other methods of building strings. So, if performance is key, then its better. However, if performance is not critical then I personally find string.Format to be easier to follow in code. (But that's a subjective reason) StringBuilder however, is probably most efficient with respect to memory utilization.

dviljoen
  • 1,612
  • 1
  • 16
  • 28
-1

String concatenation takes more memory compared to String.Format. So best way to concatenate strings is using String.Format or System.Text.StringBuilder Object.

Let's take first case: "C" + rowIndex.ToString() Let's assume rowIndex is a value type so ToString() method has to Box to convert the value to String and then CLR creates memory for the new string with both values included.

Where as string.Format expects object parameter and takes in rowIndex as an object and converts it to string internally offcourse there will be Boxing but it's intrinsic and also it's won't take up as much memory as in first case.

For short strings it won't matter that much I guess...