Допустим, вы хотите вывести или объединить строки. Какой из следующих стилей вы предпочитаете?
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 тиков
Лично, второй, поскольку все, что вы используете, находится в прямом порядке, в котором он будет выводиться. Принимая во внимание, что с первым вы должны сопоставить {0} и {1} с правильным var, что легко испортить.
По крайней мере, это не так плохо, как в C ++ sprintf, где, если вы неправильно указали тип переменной, все это взорвется.
Кроме того, поскольку второй является встроенным и не должен выполнять поиск и замену всех элементов {0}, последний должен быть быстрее ... хотя я точно не знаю.
Я думаю, что это сильно зависит от того, насколько сложным является выход. Я склонен выбирать тот сценарий, который работает лучше всего в то время.
Подберите правильный инструмент в зависимости от работы: D Какой из них выглядит чистым!
Для очень простых манипуляций я бы использовал конкатенацию, но как только вы выйдете за 2 или 3 элемента, формат станет более подходящим IMO.
Еще одна причина, по которой предпочтение отдается String.Format, заключается в том, что строки .NET являются неизменяемыми, и при таком способе создается меньше временных / промежуточных копий.
Я предпочитаю и второе, но у меня нет рациональных аргументов в поддержку этого положения.
Обычно я предпочитаю первое, так как особенно, когда строки становятся длинными, их гораздо легче читать.
Другим преимуществом является, я полагаю, одно из показателей производительности, поскольку последний фактически выполняет 2 оператора создания строки перед передачей окончательной строки в метод Console.Write. Я полагаю, что String.Format использует StringBuilder под крышками, поэтому избегают множественных конкатенаций.
Однако следует отметить, что если параметры, которые вы передаете в String.Format (и другие такие методы, как Console.Write), являются типами значений, то перед передачей они будут помещены в коробку, что может привести к снижению производительности. Сообщение в блоге об этом здесь .
Для базовой конкатенации строк я обычно использую второй стиль - легче читать и проще. Однако, если я делаю более сложную комбинацию строк, я обычно выбираю 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 + ".");
Сохранено только несколько символов, но я думаю, что в этом примере формат делает его намного чище.
Конкатенация строк хороша в таком простом сценарии - она сложнее с чем-либо более сложным, чем даже 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);
На самом деле мне нравится первый, потому что, когда в тексте много переменных, мне кажется, что их легче читать. Кроме того, легче использовать кавычки при использовании строки. Формат () форматирования. Вот достойный анализ конкатенации строк.
- Форматирование - это «.NET» способ сделать это. Некоторые инструменты рефакторинга (Refactor! For one) даже предлагают реорганизовать код в стиле concat для использования стиля форматирования.
- Форматирование легче оптимизировать для компилятора (хотя второе, вероятно, будет реорганизовано для использования быстрого метода Concat).
- Форматирование обычно более понятно для чтения (особенно при «причудливом» форматировании).
- Форматирование означает неявные вызовы '.ToString' для всех переменных, что хорошо для удобства чтения.
- Согласно «Эффективному C #», реализации .NET «WriteLine» и «Format» перепутаны, они автоматически блокируют все типы значений (что плохо). «Эффективный C #» советует явно выполнять вызовы «.ToString», которые, по моему мнению, являются поддельными (см. Публикацию Джеффа )
- На данный момент подсказки форматирования не проверяются компилятором, что приводит к ошибкам во время выполнения. Тем не менее, это может быть исправлено в будущих версиях.
Хотя я полностью понимаю предпочтения стиля и выбрал конкатенацию для своего первого ответа, частично основываясь на моих собственных предпочтениях, часть моего решения была основана на мысли, что конкатенация будет быстрее. Итак, из любопытства я проверил это, и результаты были ошеломляющими, особенно для такой маленькой нити.
Используя следующий код:
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, но у меня также была бы строка формата в файлах ресурсов, чтобы ее можно было локализовать для других языков. Использование простой строки concat не позволяет вам сделать это. Очевидно, что если вам никогда не понадобится локализовать эту строку, это не повод задуматься. Это действительно зависит от того, для чего предназначена строка.
Если это будет показано пользователю, я бы использовал String.Format, чтобы я мог локализовать, если мне нужно - и FxCop будет проверять его правописание, на всякий случай :)
Если он содержит числа или любые другие нестроковые объекты (например, даты), я бы использовал String.Format, потому что он дает мне больше контроля над форматированием .
Если это для создания запроса, такого как SQL, я бы использовал Linq .
Если бы для объединения строк внутри цикла я бы использовал StringBuilder, чтобы избежать проблем с производительностью.
Если это какой-то вывод, который пользователь не увидит и не повлияет на производительность, я бы использовал String.Format, потому что я все равно использую его, и я просто привык к нему :)
Я всегда шел по маршруту string.Format (). Возможность сохранять форматы в переменных, как в примере Натана, является большим преимуществом. В некоторых случаях я могу добавить переменную, но как только объединяется более 1 переменной, я реорганизую использование форматирования.
Да, и просто для полноты, следующее на несколько тиков быстрее обычной конкатенации:
Console.WriteLine(String.Concat(p.FirstName," ",p.LastName));
О, дорогой - после прочтения одного из других ответов я попытался изменить порядок операций, поэтому сначала выполняю конкатенацию, а затем 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!») До того, как первая операция вернет все строки в соответствие.
На самом деле, я вчера проводил эти тесты, но было уже поздно, поэтому я не ставил свои ответы.
Суть в том, что они в среднем занимают одно и то же время. Я сделал тест более 100000 итераций.
Я тоже попробую с StringBuilder и выложу код и результаты, когда вернусь домой.
Вот мои результаты за 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");
Итак, я не знаю, чей ответ пометить как ответ :)
Хороший!
Только что добавлен
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
string.Concat(...)
. Это делается во время компиляции, поэтому не влияет на производительность во время выполнения. Если вы запускаете тесты несколько раз или запускаете тестовые образцы большего размера, вы увидите, что они идентичны. Первый (формат) выглядит лучше для меня. Это более читабельно, и вы не создаете дополнительные временные строковые объекты.
Я поражен, что так много людей сразу хотят найти код, который выполняется быстрее всего. Если ОДИН МИЛЛИОН итераций ИСПОЛЬЗУЕТСЯ менее секунды для обработки, будет ли это в ЛЮБОМ СПОСОБЕ заметным для конечного пользователя? Не очень вероятно.
Преждевременная оптимизация = FAIL.
Я бы выбрал String.Format
вариант, только потому, что он имеет наибольшее значение с архитектурной точки зрения. Я не забочусь о производительности, пока она не станет проблемой (и если бы это произошло, я бы спросила себя: нужно ли мне объединять миллион имен одновременно? Конечно, они не все уместятся на экране ...)
Подумайте, хочет ли ваш клиент позже изменить его, чтобы он мог настроить, отображать "Firstname Lastname"
или "Lastname, Firstname."
с помощью параметра «Формат», это легко - просто поменяйте строку формата. С concat вам понадобится дополнительный код. Конечно, в этом конкретном примере это не имеет большого значения, но нужно экстраполировать.
Лучшим тестом было бы наблюдать за вашей памятью, используя Perfmon и счетчики памяти CLR. Насколько я понимаю, единственная причина, по которой вы хотите использовать String.Format вместо простой конкатенации строк, заключается в том, что поскольку строки являются неизменяемыми, вы без необходимости загружаете сборщик мусора временными строками, которые необходимо будет восстановить в следующем проходе.
StringBuilder и String.Format, хотя и потенциально медленнее, но более эффективны в использовании памяти.
Если вы имеете дело с чем-то, что должно быть легко читаемым (а это большая часть кода), я бы остановился на версии перегрузки оператора UNLESS:
- Код должен быть выполнен миллионы раз
- Вы делаете тонны конкатов (более 4 - это тонна)
- Код ориентирован на Компактную платформу
По крайней мере при двух из этих обстоятельств я бы вместо этого использовал StringBuilder.
Мне было любопытно, где 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 тика
Я выбираю на основе читабельности. Я предпочитаю вариант форматирования, когда вокруг переменных есть текст. В этом примере:
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 необходим, потому что разные естественные языки могут даже не иметь данные в одном и том же порядке.
Согласно подготовительному материалу MCSD, Microsoft предлагает использовать оператор + при работе с очень небольшим числом конкатенаций (вероятно, от 2 до 4). Я до сих пор не уверен, почему, но это то, что нужно учитывать.
Жаль бедных переводчиков
Если вы знаете, что ваше приложение останется на английском языке, тогда хорошо, сохраните отметки времени. Тем не менее, многие культуры обычно видят Фамилию Имя, например, по адресам.
Так что пользуйтесь string.Format()
, особенно если вы когда-нибудь отправите свое приложение куда-нибудь, где английский не будет родным языком.
string.Format()
вести себя по-разному в разных культурах? Не будет ли по-прежнему печататься имя, затем фамилия? Похоже, что вам придется принимать во внимание различную культуру в обеих ситуациях. Я чувствую, что я что-то здесь упускаю. string.Format()
знать, что вы используете имя для адреса? Если string.Format()
поменять местами {0} {1}
на основе культуры, я бы посчитал это сломанным. Строки являются неизменяемыми, это означает, что один и тот же крошечный фрагмент памяти используется снова и снова в вашем коде. Добавление тех же двух строк вместе и создание одной и той же новой строки снова и снова не влияет на память. .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
стоит крошечной производительности здесь. Архитектурно это лучше, так как это означает, что вы можете изменить формат более легко. Но струнный строитель я действительно не вижу смысла. Любой другой поток здесь говорит, что вы должны использовать Stringbuilder вместо объединения строк. В чем преимущество? Очевидно, не скорость, как доказывает этот тест. Поскольку я не думаю, что ответы здесь охватывают все, я хотел бы сделать небольшое дополнение здесь.
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) или просто «распределить-и-добавить».
Начиная с C # 6.0, для этого можно использовать интерполированные строки , что еще больше упрощает формат.
var name = "Bill";
var surname = "Gates";
MessageBox.Show($"Welcome to the show, {name} {surname}!");
Интерполированное строковое выражение выглядит как строка шаблона, содержащая выражения. Интерполированное строковое выражение создает строку, заменяя содержащиеся выражения на представления ToString результатов выражений.
Интерполированные строки имеют производительность, аналогичную String.Format, но улучшают читаемость и сокращают синтаксис из-за того, что значения и выражения вставляются в строку.
Пожалуйста, также обратитесь к этой статье dotnetperls по интерполяции строк.
Если вы ищете способ форматирования ваших строк по умолчанию, это имеет смысл с точки зрения читабельности и производительности (за исключением случаев, когда микросекунды будут иметь значение в вашем конкретном случае использования).
string.Format
, который не использует какие-либо функции составного форматирования (то есть, просто{0}
) и заменить их значительно более быстрой конкатенацией строк. Интересно, что такой подвиг достижим с существующим переписчиком IL, таким как PostSharp.