String output: format or concat in C#?

前端 未结 30 1658
一生所求
一生所求 2020-11-22 11:40

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 = \"Ga

相关标签:
30条回答
  • 2020-11-22 12:00

    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 a 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 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 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 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 uses StringBuilder 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.

    0 讨论(0)
  • 2020-11-22 12:01

    I'm amazed that so many people immediately want to find the code that executes the fastest. If ONE MILLION iterations STILL take less than a second to process, is this going to be in ANY WAY noticeable to the end user? Not very likely.

    Premature optimization = FAIL.

    I'd go with the String.Format option, only because it makes the most sense from an architectural standpoint. I don't care about the performance until it becomes an issue (and if it did, I'd ask myself: Do I need to concatenate a million names at once? Surely they won't all fit on the screen...)

    Consider if your customer later wants to change it so that they can configure whether to display "Firstname Lastname" or "Lastname, Firstname." With the Format option, this is easy - just swap out the format string. With the concat, you'll need extra code. Sure that doesn't sound like a big deal in this particular example but extrapolate.

    0 讨论(0)
  • 2020-11-22 12:01

    Oh dear - after reading one of the other replies I tried reversing the order of the operations - so performing the concatenation first, then the String.Format...

    Bill Gates
    Console.WriteLine(p.FirstName + " " + p.LastName); took: 8ms - 30488 ticks
    Bill Gates
    Console.WriteLine("{0} {1}", p.FirstName, p.LastName); took: 0ms - 182 ticks
    

    So the order of the operations makes a HUGE difference, or rather the very first operation is ALWAYS much slower.

    Here is the results of a run where operations are completed more than once. I have tried changing the orders but things generally follow the same rules, once the first result is ignored:

    Bill Gates
    Console.WriteLine(FirstName + " " + LastName); took: 5ms - 20335 ticks
    Bill Gates
    Console.WriteLine(FirstName + " " + LastName); took: 0ms - 156 ticks
    Bill Gates
    Console.WriteLine(FirstName + " " + LastName); took: 0ms - 122 ticks
    Bill Gates
    Console.WriteLine("{0} {1}", FirstName, LastName); took: 0ms - 181 ticks
    Bill Gates
    Console.WriteLine("{0} {1}", FirstName, LastName); took: 0ms - 122 ticks
    Bill Gates
    String.Concat(FirstName, " ", LastName); took: 0ms - 142 ticks
    Bill Gates
    String.Concat(FirstName, " ", LastName); took: 0ms - 117 ticks
    

    As you can see subsequent runs of the same method (I refactored the code into 3 methods) are incrementally faster. The fastest appears to be the Console.WriteLine(String.Concat(...)) method, followed by normal concatenation, and then the formatted operations.

    The initial delay in startup is likely the initialisation of Console Stream, as placing a Console.Writeline("Start!") before the first operation brings all times back into line.

    0 讨论(0)
  • 2020-11-22 12:01

    Oh, and just for completeness, the following is a few ticks faster than normal concatenation:

    Console.WriteLine(String.Concat(p.FirstName," ",p.LastName));
    
    0 讨论(0)
  • 2020-11-22 12:02

    For basic string concatenation, I generally use the second style - easier to read and simpler. However, if I am doing a more complicated string combination I usually opt for String.Format.

    String.Format saves on lots of quotes and pluses...

    Console.WriteLine("User {0} accessed {1} on {2}.", user.Name, fileName, timestamp);
    vs
    Console.WriteLine("User " + user.Name + " accessed " + fileName + " on " + timestamp + ".");
    

    Only a few charicters saved, but I think, in this example, format makes it much cleaner.

    0 讨论(0)
  • 2020-11-22 12:02

    Personally, the second one as everything you are using is in the direct order it will be output in. Whereas with the first you have to match up the {0} and {1} with the proper var, which is easy to mess up.

    At least it's not as bad as the C++ sprintf where if you get the variable type wrong the whole thing will blow up.

    Also, since the second is all inline and it doesn't have to do any searching and replacing for all the {0} things, the latter should be faster... though I don't know for sure.

    0 讨论(0)
提交回复
热议问题