Вывод строки: формат или конкат в C#?

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

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

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

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

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

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

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

32 ответа

Решение

Попробуйте этот код.

Это слегка измененная версия вашего кода.
1. Я удалил Console.WriteLine, поскольку он, вероятно, на несколько порядков медленнее, чем то, что я пытаюсь измерить.
2. Я запускаю секундомер перед циклом и останавливаю его сразу после этого, так что я не теряю точности, если для выполнения функции требуется, например, 26,4 такта.
3. То, как вы разделили результат на несколько итераций, было неверным. Посмотрите, что произойдет, если у вас есть 1000 миллисекунд и 100 миллисекунд. В обеих ситуациях вы получите 0 мс после деления на 1000000.

Stopwatch s = new Stopwatch();

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

int n = 1000000;
long fElapsedMilliseconds = 0, fElapsedTicks = 0, cElapsedMilliseconds = 0, cElapsedTicks = 0;

string result;
s.Start();
for (var i = 0; i < n; i++)
    result = (p.FirstName + " " + p.LastName);
s.Stop();
cElapsedMilliseconds = s.ElapsedMilliseconds;
cElapsedTicks = s.ElapsedTicks;
s.Reset();
s.Start();
for (var i = 0; i < n; i++)
    result = string.Format("{0} {1}", p.FirstName, p.LastName);
s.Stop();
fElapsedMilliseconds = s.ElapsedMilliseconds;
fElapsedTicks = s.ElapsedTicks;
s.Reset();


Console.Clear();
Console.WriteLine(n.ToString()+" x result = string.Format(\"{0} {1}\", p.FirstName, p.LastName); took: " + (fElapsedMilliseconds) + "ms - " + (fElapsedTicks) + " ticks");
Console.WriteLine(n.ToString() + " x result = (p.FirstName + \" \" + p.LastName); took: " + (cElapsedMilliseconds) + "ms - " + (cElapsedTicks) + " ticks");
Thread.Sleep(4000);

Это мои результаты:

1000000 x result = string.Format ("{0} {1}", p.FirstName, p.LastName); заняло: 618мс - 2213706 тиков
1000000 x result = (p.FirstName + " " + p.LastName); заняло: 166мс - 595610 тиков

Я поражен, что так много людей сразу хотят найти код, который выполняется быстрее всего. Если ОДИН МИЛЛИОН итераций ИСПОЛЬЗУЕТСЯ меньше секунды, будет ли это ЛЮБОЙ СПОСОБ заметен конечному пользователю? Не очень вероятно.

Преждевременная оптимизация = FAIL.

Я бы пошел с String.Format вариант, только потому, что это имеет больше смысла с архитектурной точки зрения. Я не забочусь о производительности, пока она не станет проблемой (и если бы это произошло, я бы спросила себя: нужно ли мне объединять миллион имен одновременно? Конечно, они не будут все помещаться на экране...)

Подумайте, хочет ли ваш клиент позже изменить его, чтобы он мог настроить, отображать ли "Firstname Lastname" или же "Lastname, Firstname." С опцией Формат это легко - просто поменяйте строку формата. С concat вам понадобится дополнительный код. Конечно, в данном конкретном примере это не имеет большого значения, но нужно экстраполировать.

О, дорогой - после прочтения одного из других ответов я попытался изменить порядок операций, поэтому сначала выполняю конкатенацию, а затем 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

Таким образом, порядок операций имеет ОГРОМНОЕ различие, или, скорее, самая первая операция ВСЕГДА намного медленнее.

Вот результаты прогона, в котором операции выполняются более одного раза. Я пытался изменить порядок, но, как правило, все по тем же правилам, как только первый результат игнорируется:

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

Как вы можете видеть, последующие прогоны того же метода (я преобразовал код в 3 метода) пошагово быстрее. Наиболее быстрым представляется метод Console.WriteLine(String.Concat(...)), за которым следует обычная конкатенация и затем отформатированные операции.

Первоначальная задержка при запуске, вероятно, является инициализацией Console Stream, так как установка Console.Writeline("Start!") До того, как первая операция вернет все строки в соответствие.

Строки являются неизменяемыми, это означает, что один и тот же крошечный фрагмент памяти используется в вашем коде снова и снова. Добавление тех же двух строк вместе и создание одной и той же новой строки снова и снова не влияет на память. .Net достаточно умен, чтобы использовать одну и ту же ссылку на память. Поэтому ваш код не проверяет разницу между двумя методами concat.

Попробуйте это для размера:

Stopwatch s = new Stopwatch();

int n = 1000000;
long fElapsedMilliseconds = 0, fElapsedTicks = 0, cElapsedMilliseconds = 0, cElapsedTicks = 0, sbElapsedMilliseconds = 0, sbElapsedTicks = 0;

Random random = new Random(DateTime.Now.Millisecond);

string result;
s.Start();
for (var i = 0; i < n; i++)
    result = (random.Next().ToString() + " " + random.Next().ToString());
s.Stop();
cElapsedMilliseconds = s.ElapsedMilliseconds;
cElapsedTicks = s.ElapsedTicks;
s.Reset();

s.Start();
for (var i = 0; i < n; i++)
    result = string.Format("{0} {1}", random.Next().ToString(), random.Next().ToString());
s.Stop();
fElapsedMilliseconds = s.ElapsedMilliseconds;
fElapsedTicks = s.ElapsedTicks;
s.Reset();

StringBuilder sb = new StringBuilder();
s.Start();
for(var i = 0; i < n; i++){
    sb.Clear();
    sb.Append(random.Next().ToString());
    sb.Append(" ");
    sb.Append(random.Next().ToString());
    result = sb.ToString();
}
s.Stop();
sbElapsedMilliseconds = s.ElapsedMilliseconds;
sbElapsedTicks = s.ElapsedTicks;
s.Reset();

Console.WriteLine(n.ToString() + " x result = string.Format(\"{0} {1}\", p.FirstName, p.LastName); took: " + (fElapsedMilliseconds) + "ms - " + (fElapsedTicks) + " ticks");
Console.WriteLine(n.ToString() + " x result = (p.FirstName + \" \" + p.LastName); took: " + (cElapsedMilliseconds) + "ms - " + (cElapsedTicks) + " ticks");
Console.WriteLine(n.ToString() + " x sb.Clear();sb.Append(random.Next().ToString()); sb.Append(\" \"); sb.Append(random.Next().ToString()); result = sb.ToString(); took: " + (sbElapsedMilliseconds) + "ms - " + (sbElapsedTicks) + " ticks");
Console.WriteLine("****************");
Console.WriteLine("Press Enter to Quit");
Console.ReadLine();

Пример вывода:

1000000 x result = string.Format("{0} {1}", p.FirstName, p.LastName); took: 513ms - 1499816 ticks
1000000 x result = (p.FirstName + " " + p.LastName); took: 393ms - 1150148 ticks
1000000 x sb.Clear();sb.Append(random.Next().ToString()); sb.Append(" "); sb.Append(random.Next().ToString()); result = sb.ToString(); took: 405ms - 1185816 ticks

Жаль бедных переводчиков

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

Так что используйте string.Format()особенно если вы когда-нибудь захотите, чтобы ваше приложение отправлялось куда-нибудь, где английский не является родным языком.

Вот мои результаты за 100000 итераций:

Console.WriteLine("{0} {1}", p.FirstName, p.LastName); took (avg): 0ms - 689 ticks
Console.WriteLine(p.FirstName + " " + p.LastName); took (avg): 0ms - 683 ticks

И вот код стенда:

Stopwatch s = new Stopwatch();

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

//First print to remove the initial cost
Console.WriteLine(p.FirstName + " " + p.LastName);
Console.WriteLine("{0} {1}", p.FirstName, p.LastName);

int n = 100000;
long fElapsedMilliseconds = 0, fElapsedTicks = 0, cElapsedMilliseconds = 0, cElapsedTicks = 0;

for (var i = 0; i < n; i++)
{
    s.Start();
    Console.WriteLine(p.FirstName + " " + p.LastName);
    s.Stop();
    cElapsedMilliseconds += s.ElapsedMilliseconds;
    cElapsedTicks += s.ElapsedTicks;
    s.Reset();
    s.Start();
    Console.WriteLine("{0} {1}", p.FirstName, p.LastName);
    s.Stop();
    fElapsedMilliseconds += s.ElapsedMilliseconds;
    fElapsedTicks += s.ElapsedTicks;
    s.Reset();
}

Console.Clear();

Console.WriteLine("Console.WriteLine(\"{0} {1}\", p.FirstName, p.LastName); took (avg): " + (fElapsedMilliseconds / n) + "ms - " + (fElapsedTicks / n) + " ticks");
Console.WriteLine("Console.WriteLine(p.FirstName + \" \" + p.LastName); took (avg): " + (cElapsedMilliseconds / n) + "ms - " + (cElapsedTicks / n) + " ticks");

Итак, я не знаю, чей ответ пометить как ответ:)

Конкатенация строк хороша в таком простом сценарии - она ​​сложнее с чем-либо более сложным, даже чем LastName, FirstName. С форматом, который вы можете сразу увидеть, какой будет окончательная структура строки при чтении кода, с конкатенацией становится практически невозможно сразу увидеть конечный результат (за исключением очень простого примера, подобного этому).

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

Если вы используете.NET 3.5, вы можете использовать метод расширения, подобный этому, и получить легкий переход от синтаксиса манжеты, например:

string str = "{0} {1} is my friend. {3}, {2} is my boss.".FormatWith(prop1,prop2,prop3,prop4);

Наконец, по мере того как ваше приложение усложняется, вы можете решить, что для разумного сохранения строк в вашем приложении вы хотите переместить их в файл ресурсов для локализации или просто в статический помощник. Это будет НАМНОГО легче достигнуть, если вы будете последовательно использовать форматы, и ваш код может быть просто реорганизован, чтобы использовать что-то вроде

string name = String.Format(ApplicationStrings.General.InformalUserNameFormat,this.FirstName,this.LastName);

Начиная с C# 6.0, для этого можно использовать интерполированные строки, что еще больше упрощает формат.

var name = "Bill";
var surname = "Gates";
MessageBox.Show($"Welcome to the show, {name} {surname}!");

Интерполированное строковое выражение выглядит как шаблонная строка, содержащая выражения. Интерполированное строковое выражение создает строку, заменяя содержащиеся выражения на представления ToString результатов выражений.

Интерполированные строки имеют производительность, аналогичную String.Format, но улучшают удобочитаемость и сокращают синтаксис из-за того, что значения и выражения вставляются в строку.

Пожалуйста, также обратитесь к этой статье dotnetperls по интерполяции строк.

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

Для очень простых манипуляций я бы использовал конкатенацию, но как только вы выйдете за 2 или 3 элемента, формат становится более подходящим IMO.

Еще одна причина, по которой предпочтение отдается String.Format, заключается в том, что строки.NET являются неизменяемыми, и при таком способе создается меньше временных / промежуточных копий.

Хотя я полностью понимаю предпочтения стиля и выбрал конкатенацию для своего первого ответа, частично основываясь на моих собственных предпочтениях, часть моего решения была основана на мысли, что конкатенация будет быстрее. Итак, из любопытства я проверил это, и результаты были ошеломляющими, особенно для такой маленькой нити.

Используя следующий код:

    System.Diagnostics.Stopwatch s = new System.Diagnostics.Stopwatch();

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

    s.Start();
    Console.WriteLine("{0} {1}", p.FirstName, p.LastName);
    s.Stop();
    Console.WriteLine("Console.WriteLine(\"{0} {1}\", p.FirstName, p.LastName); took: " + s.ElapsedMilliseconds + "ms - " + s.ElapsedTicks + " ticks");

    s.Reset();
    s.Start();
    Console.WriteLine(p.FirstName + " " + p.LastName);
    s.Stop();

    Console.WriteLine("Console.WriteLine(p.FirstName + \" \" + p.LastName); took: " + s.ElapsedMilliseconds + "ms - " + s.ElapsedTicks + " ticks");

Я получил следующие результаты:

Bill Gates
Console.WriteLine("{0} {1}", p.FirstName, p.LastName); took: 2ms - 7280 ticks
Bill Gates
Console.WriteLine(p.FirstName + " " + p.LastName); took: 0ms - 67 ticks

Использование метода форматирования более чем в 100 раз медленнее! Конкатенация даже не регистрировалась как 1 мс, поэтому я также выводил тики таймера.

Для базовой конкатенации строк я обычно использую второй стиль - легче читать и проще. Однако, если я делаю более сложную комбинацию строк, я обычно выбираю String.Format.

String.Format экономит на большом количестве цитат и плюсов...

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

Сохранено только несколько символов, но я думаю, что в этом примере формат делает его намного чище.

Лучшим тестом было бы наблюдать за вашей памятью, используя Perfmon и счетчики памяти CLR. Насколько я понимаю, единственная причина, по которой вы хотите использовать String.Format вместо простой конкатенации строк, заключается в том, что, поскольку строки являются неизменяемыми, вы без необходимости загружаете сборщик мусора временными строками, которые необходимо восстановить в следующем проходе.

StringBuilder и String.Format, хотя и потенциально медленнее, но более эффективны в использовании памяти.

Что плохого в конкатенации строк?

Через неделю, 19 августа 2015 года, этому вопросу будет ровно семь (7) лет. Теперь есть лучший способ сделать это. Лучше с точки зрения удобства сопровождения, поскольку я не проводил никаких тестов производительности по сравнению с простым конкатенацией строк (но имеет ли это значение в наши дни - разница в несколько миллисекунд?). Новый способ сделать это с C# 6.0:

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

Эта новая функция лучше, IMO, и на самом деле лучше в нашем случае, поскольку у нас есть коды, в которых мы строим строки запросов, значения которых зависят от некоторых факторов. Представьте себе одну строку запроса, где у нас есть 6 аргументов. Так что вместо того, чтобы сделать, например:

var qs = string.Format("q1={0}&q2={1}&q3={2}&q4={3}&q5={4}&q6={5}", 
    someVar, anotherVarWithLongName, var3, var4, var5, var6)

в может быть написано так, и это легче читать:

var qs=$"q1={someVar}&q2={anotherVarWithLongName}&q3={var3}&q4={var4}&q5={var5}&q6={var6}";

Обычно я предпочитаю первое, так как особенно когда строки становятся длиннее, их гораздо легче читать.

Другим преимуществом является, я полагаю, одно из показателей производительности, поскольку последнее фактически выполняет 2 оператора создания строки перед передачей окончательной строки в метод Console.Write. Я полагаю, что String.Format использует StringBuilder под крышками, поэтому избегают множественных конкатенаций.

Однако следует отметить, что если параметры, которые вы передаете в String.Format (и другие такие методы, как Console.Write), являются типами значений, то перед передачей они будут помещены в коробку, что может привести к снижению производительности. Сообщение в блоге об этом здесь.

  1. Форматирование - это ".NET" способ сделать это. Некоторые инструменты рефакторинга (Refactor! For one) даже предложат рефакторинг кода в стиле concat для использования стиля форматирования.
  2. Форматирование легче оптимизировать для компилятора (хотя второе, вероятно, будет подвергнуто рефакторингу для использования быстрого метода Concat).
  3. Форматирование обычно более понятно для чтения (особенно при "причудливом" форматировании).
  4. Форматирование означает неявные вызовы '.ToString' для всех переменных, что хорошо для удобства чтения.
  5. Согласно "Эффективному C#", реализации.NET "WriteLine" и "Format" испорчены, они автоматически блокируют все типы значений (что плохо). "Эффективный C#" советует явно выполнять вызовы ".ToString", которые, по моему мнению, являются поддельными (см. Публикацию Джеффа)
  6. На данный момент подсказки форматирования не проверяются компилятором, что приводит к ошибкам во время выполнения. Тем не менее, это может быть исправлено в будущих версиях.

Я бы использовал String.Format, но у меня также была бы строка формата в файлах ресурсов, чтобы ее можно было локализовать для других языков. Использование простой строки concat не позволяет вам сделать это. Очевидно, что если вам никогда не понадобится локализовать эту строку, это не повод задуматься. Это действительно зависит от того, для чего предназначена строка.

Если это будет показано пользователю, я бы использовал String.Format, чтобы я мог локализовать, если мне нужно - и FxCop проверит его правописание, на всякий случай:)

Если он содержит числа или любые другие нестроковые объекты (например, даты), я бы использовал String.Format, потому что он дает мне больше контроля над форматированием.

Если это для построения запроса, такого как SQL, я бы использовал Linq.

Если бы для объединения строк внутри цикла я бы использовал StringBuilder, чтобы избежать проблем с производительностью.

Если это какой-то вывод, который пользователь не увидит и не повлияет на производительность, я бы использовал String.Format, потому что я все равно использую его и просто привык к нему:)

Я выбираю на основе читабельности. Я предпочитаю вариант форматирования, когда вокруг переменных есть текст. В этом примере:

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

Вы понимаете значение даже без имен переменных, тогда как concat загроможден кавычками и знаками + и смущает мои глаза:

Console.WriteLine("User " + user.Name + " accessed " + fileName + 
                  " on " + timestamp + ".");

(Я позаимствовал пример Майка, потому что мне это нравится)

Если строка формата не имеет большого значения без имен переменных, я должен использовать concat:

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

Опция форматирования позволяет мне прочитать имена переменных и сопоставить их с соответствующими номерами. Для параметра concat этого не требуется. Меня все еще смущают кавычки и знаки +, но альтернатива еще хуже. Рубин?

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

С точки зрения производительности, я ожидаю, что параметр format будет медленнее, чем concat, поскольку форматирование требует разбора строки. Я не помню, чтобы оптимизировать такого рода инструкции, но если бы я это сделал, я бы посмотрел на string методы, такие как Concat() а также Join(),

Другое преимущество формата состоит в том, что строку формата можно поместить в файл конфигурации. Очень удобно с сообщениями об ошибках и текстом пользовательского интерфейса.

Если вы намереваетесь локализовать результат, то String.Format необходим, потому что разные естественные языки могут даже не иметь данные в одном и том же порядке.

Если вы имеете дело с чем-то, что должно быть легко читаемым (а это большая часть кода), я бы остановился на версии перегрузки оператора UNLESS:

  • Код должен быть выполнен миллионы раз
  • Вы делаете тонны конкатов (более 4 - это тонна)
  • Код ориентирован на Компактную платформу

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

Хороший!

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

        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

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

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 конкатата '+' будет отлично работать для 1 конкатата. И если вы не заботитесь о оптимизации микро-производительности, 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/wo culture) или просто "распределить-и-добавить".

Я предпочитаю и второе, но у меня нет рациональных аргументов в поддержку этого положения.

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

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

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

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

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

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

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

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

Мне было любопытно, где 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;
   }
}

Результаты:

Конкат: 406 тиков
Конкат: 356 тиков
Конкат: 411 тиков
Конкат: 299 тиков
Конкат: 266 тиков
Формат: 5269 тиков
Формат: 954 галочки
Формат: 1004 галочки
Формат: 984 галочки
Формат: 974 галочки
StringBuilder: 629 тиков
StringBuilder: 484 тика
StringBuilder: 482 тика
StringBuilder: 508 тиков
StringBuilder: 504 тика

Наиболее читаемым было бы использование функции интерполяции строк в C#. 6.0:

      Console.WriteLine($"{p.FirstName} {p.LastName}");

Его производительность аналогична использованию "+".

Другие вопросы по тегам