Вывод строки: формат или конкат в C #? - PullRequest
175 голосов
/ 19 августа 2008

Допустим, вы хотите вывести или объединить строки. Какой из следующих стилей вы предпочитаете?

  • var p = new { FirstName = "Bill", LastName = "Gates" };

  • Console.WriteLine("{0} {1}", p.FirstName, p.LastName);

  • Console.WriteLine(p.FirstName + " " + p.LastName);

Вы предпочитаете использовать формат или просто строки? Что твое любимое? Один из них болит твои глаза?

Есть ли у вас рациональные аргументы в пользу использования одного, а не другого?

Я бы пошел на второй.

Ответы [ 31 ]

2 голосов
/ 20 августа 2008

Хороший!

Только что добавлено

        s.Start();
        for (var i = 0; i < n; i++)
            result = string.Concat(p.FirstName, " ", p.LastName);
        s.Stop();
        ceElapsedMilliseconds = s.ElapsedMilliseconds;
        ceElapsedTicks = s.ElapsedTicks;
        s.Reset();

И это даже быстрее (я думаю, что string.Concat вызывается в обоих примерах, но первый требует некоторого перевода).

1000000 x result = string.Format("{0} {1}", p.FirstName, p.LastName); took: 249ms - 3571621 ticks
1000000 x result = (p.FirstName + " " + p.LastName); took: 65ms - 944948 ticks
1000000 x result = string.Concat(p.FirstName, " ", p.LastName); took: 54ms - 780524 ticks
2 голосов
/ 19 августа 2008

Я думаю, что это сильно зависит от сложности вывода. Я склонен выбирать тот сценарий, который лучше всего работает в данный момент.

Подберите подходящий инструмент в зависимости от работы: D Какой бы ни выглядел самый чистый!

2 голосов
/ 22 мая 2015

Поскольку я не думаю, что ответы здесь охватывают все, я хотел бы сделать небольшое дополнение здесь.

Console.WriteLine(string format, params object[] pars) звонки string.Format. «+» Подразумевает конкатенацию строк. Я не думаю, что это всегда связано со стилем; Я склонен смешивать два стиля в зависимости от контекста, в котором я нахожусь.

Краткий ответ

Решение, с которым вы сталкиваетесь, связано с распределением строк. Я постараюсь сделать это просто.

Скажем, у вас есть

string s = a + "foo" + b;

Если вы выполните это, оно будет оцениваться следующим образом:

string tmp1 = a;
string tmp2 = "foo" 
string tmp3 = concat(tmp1, tmp2);
string tmp4 = b;
string s = concat(tmp3, tmp4);

tmp здесь на самом деле не локальная переменная, а временная для JIT (она помещается в стек IL). Если вы помещаете строку в стек (например, ldstr в IL для литералов), вы помещаете ссылку на указатель строки в стеке.

В тот момент, когда вы вызываете concat, эта ссылка становится проблемой, потому что нет доступной строковой ссылки, содержащей обе строки. Это означает, что .NET необходимо выделить новый блок памяти, а затем заполнить его двумя строками. Причина в том, что это проблема, потому что распределение относительно дорого.

Что меняет вопрос на: Как вы можете уменьшить количество операций concat?

Итак, грубый ответ: string.Format для> 1 concats, '+' будет отлично работать для 1 concat. И если вам не нужны микропроцессорные оптимизации, string.Format будет работать нормально в общем случае.

Записка о культуре

А потом есть нечто, называемое культурой ...

string.Format позволяет использовать CultureInfo в вашем форматировании. Простой оператор «+» использует текущую культуру.

Это особенно важное замечание, если вы пишете форматы файлов и f.ex. double значения, которые вы «добавляете» в строку. На разных машинах вы можете получить разные строки, если вы не используете string.Format с явным CultureInfo.

f.ex. подумайте, что произойдет, если вы измените «.» для ',' при написании файла с разделенными запятыми значениями ... на голландском языке десятичный разделитель - запятая, поэтому ваш пользователь может просто получить «забавный» сюрприз.

Более подробный ответ

Если вы заранее не знаете точный размер строки, лучше использовать такую ​​политику, чтобы перераспределить используемые вами буферы. Сначала свободное место заполняется, после чего данные копируются.

Увеличение означает выделение нового блока памяти и копирование старых данных в новый буфер. Старый блок памяти может быть освобожден. В этом пункте вы получаете практический результат: выращивание является дорогостоящей операцией.

Наиболее практичный способ сделать это - использовать политику перераспределения. Наиболее распространенной политикой является перераспределение буферов со степенями 2. Конечно, вы должны сделать это немного умнее, чем это (так как нет смысла расти с 1,2,4,8, если вы уже знаете, что вам нужно 128 символов) ) но вы получите картину. Политика гарантирует, что вам не нужно слишком много дорогостоящих операций, которые я описал выше.

StringBuilder - это класс, который в основном перераспределяет базовый буфер в степени два. string.Format использует StringBuilder под капотом.

Это делает ваше решение основным компромиссом между overallocate-and-append (-multiple) (w / w.o. Culture) или просто allocate-and-append.

1 голос
/ 19 августа 2008

Мне действительно нравится первый, потому что, когда в тексте смешано много переменных, мне легче читать. Кроме того, легче использовать кавычки при использовании строки. Формат () форматирования. Вот достойный анализ конкатенации строк.

1 голос
/ 05 июля 2009

Согласно подготовительному материалу MCSD, Microsoft предлагает использовать оператор + при работе с очень небольшим числом конкатенаций (вероятно, от 2 до 4). Я до сих пор не уверен, почему, но это то, что нужно учитывать.

1 голос
/ 22 сентября 2008

Мне было любопытно, где StringBuilder стоял с этими тестами. Результаты ниже ...

class Program {
   static void Main(string[] args) {

      var p = new { FirstName = "Bill", LastName = "Gates" };

      var tests = new[] {
         new { Name = "Concat", Action = new Action(delegate() { string x = p.FirstName + " " + p.LastName; }) },
         new { Name = "Format", Action = new Action(delegate() { string x = string.Format("{0} {1}", p.FirstName, p.LastName); }) },
         new { Name = "StringBuilder", Action = new Action(delegate() {
            StringBuilder sb = new StringBuilder();
            sb.Append(p.FirstName);
            sb.Append(" ");
            sb.Append(p.LastName);
            string x = sb.ToString();
         }) }
      };

      var Watch = new Stopwatch();
      foreach (var t in tests) {
         for (int i = 0; i < 5; i++) {
            Watch.Reset();
            long Elapsed = ElapsedTicks(t.Action, Watch, 10000);
            Console.WriteLine(string.Format("{0}: {1} ticks", t.Name, Elapsed.ToString()));
         }
      }
   }

   public static long ElapsedTicks(Action ActionDelg, Stopwatch Watch, int Iterations) {
      Watch.Start();
      for (int i = 0; i < Iterations; i++) {
         ActionDelg();
      }
      Watch.Stop();
      return Watch.ElapsedTicks / Iterations;
   }
}

Результаты:

Concat: 406 ticks
Concat: 356 ticks
Concat: 411 ticks
Concat: 299 ticks
Concat: 266 ticks
Format: 5269 ticks
Format: 954 ticks
Format: 1004 ticks
Format: 984 ticks
Format: 974 ticks
StringBuilder: 629 ticks
StringBuilder: 484 ticks
StringBuilder: 482 ticks
StringBuilder: 508 ticks
StringBuilder: 504 ticks
1 голос
/ 19 августа 2008

Я всегда шел по маршруту. Формат (). Возможность хранить форматы в переменных, как в примере Натана, является большим преимуществом. В некоторых случаях я могу добавить переменную, но как только объединяется более 1 переменной, я реорганизую использование форматирования.

1 голос
/ 23 августа 2008

Первый (формат) мне кажется лучше. Он более читабелен, и вы не создаете дополнительные временные строковые объекты.

1 голос
/ 19 августа 2008

Лично второй, поскольку все, что вы используете, находится в прямом порядке, в котором он будет выводиться. Тогда как с первым вы должны сопоставить {0} и {1} с надлежащей переменной, что легко испортить.

По крайней мере, это не так плохо, как в C ++ sprintf, где, если вы неправильно указали тип переменной, все это взорвется.

Кроме того, поскольку второй является встроенным и не должен выполнять поиск и замену всех {0} элементов, последний должен быть быстрее ... хотя я точно не знаю.

1 голос
/ 20 августа 2008

Да, и просто для полноты, следующее на несколько тиков быстрее обычной конкатенации:

Console.WriteLine(String.Concat(p.FirstName," ",p.LastName));
...