Вопрос

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

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

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

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

Вы предпочитаете использовать формат или просто объединяете строки?Какой твой любимый?Что-то из этого режет ваши глаза?

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

Я бы выбрал второй.

Это было полезно?

Решение

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

Это слегка измененная версия вашего кода.
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 результат = (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.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(), особенно если вы собираетесь разместить свое приложение где-нибудь, где английский не является основным языком.

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

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);

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

Еще одна причина предпочесть 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, хотя и потенциально медленнее, но более эффективно используют память.

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

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

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

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

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

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

Эта новая функция лучше, ИМО и вообще-то лучше в нашем случае поскольку у нас есть коды, в которых мы строим строки запросов, значения которых зависят от некоторых факторов.Представьте себе одну строку запроса, в которой у нас есть 6 аргументов.Поэтому вместо того, чтобы делать, например:

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

in можно записать так, и его будет легче читать:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • Код необходимо выполнить миллионы раз
  • Вы делаете тонны конкатов (более 4 — это тонна)
  • Код ориентирован на Compact Framework.

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

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

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

смысл понимаешь даже без названий переменных, а конкат загромождён кавычками и знаками + и путает глаза:

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);

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

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

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

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

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

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

Хороший!

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

        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.

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

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

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

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

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

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

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

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

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

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

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

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

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

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;
   }
}

Полученные результаты:

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top