String output: format or concat in C#?

前端 未结 30 1641
一生所求
一生所求 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.

提交回复
热议问题