Это Струна.Формат так же эффективен, как StringBuilder
Предположим, что у меня есть stringbuilder в C#, который делает это:
StringBuilder sb = new StringBuilder();
string cat = "cat";
sb.Append("the ").Append(cat).(" in the hat");
string s = sb.ToString();
Было бы это так же эффективно или еще более эффективно, как иметь:
string cat = "cat";
string s = String.Format("The {0} in the hat", cat);
Если да, то почему?
EDIT
После нескольких интересных ответов я понял, что мне, вероятно, следовало быть немного более ясным в том, что я спрашивал. Я не столько спрашивал, какая из них быстрее сцепляет строку, но какая быстрее инъекции одна нить в другую.В обоих случаях выше я хочу ввести одну или несколько строк в середину предопределенной строки шаблона.
Извините за путаницу
12 ответов:
String.Format
используетStringBuilder
внутренне:public static string Format(IFormatProvider provider, string format, params object[] args) { if ((format == null) || (args == null)) { throw new ArgumentNullException((format == null) ? "format" : "args"); } StringBuilder builder = new StringBuilder(format.Length + (args.Length * 8)); builder.AppendFormat(provider, format, args); return builder.ToString(); }
Приведенный выше код является фрагментом из mscorlib, поэтому возникает вопрос: "является ли
StringBuilder.Append()
быстрее, чемStringBuilder.AppendFormat()
"?Без бенчмаркинга я бы, вероятно, сказал, что приведенный выше пример кода будет работать быстрее, используя
Этот парень, Джерри Диксон, провел сравнительный анализ:.Append()
. Но это предположение, попробуйте провести сравнительный анализ и / или профилирование этих двух, чтобы получить правильное сравнение.Http://jdixon.dotnetdevelopersjournal.com/string_concatenation_stringbuilder_and_stringformat.htm
Обновлено:
К сожалению, ссылка выше с тех пор умерла. Однако есть еще копия на обратной машине:В конце дня все зависит от того, будет ли ваша строка форматирование будет вызываться повторяющимся образом, то есть вы выполняете серьезную обработку текста более 100 мегабайт текста, или вызывается ли он, когда пользователь нажимает кнопку время от времени. Если вы не занимаетесь какой-то огромной пакетной обработкой, я бы придерживался строки.Формат, он помогает читабельности кода. Если вы подозреваете узкое место perf, то вставьте профилировщик в свой код и посмотрите, где он на самом деле находится.
Из документации MSDN:
Выполнение операции конкатенации для объекта String или StringBuilder зависит от частоты выделения памяти. Операция конкатенации строк всегда выделяет память, в то время как операция конкатенации StringBuilder выделяет память только в том случае, если буфер объекта StringBuilder слишком мал для размещения новых данных. Следовательно, класс String предпочтительнее для операции конкатенации, если фиксированное число Строковые объекты объединяются. В этом случае отдельные операции конкатенации могут быть даже объединены компилятором в одну операцию. Объект StringBuilder предпочтителен для операции конкатенации, если связывается произвольное число строк; например, если цикл связывает случайное число строк пользовательского ввода.
Я провел несколько быстрых тестов производительности, и для 100 000 операций, усредненных по 10 запускам, первый метод (String Builder) занимает почти половину времени второго (String Format).
Так что, если это происходит нечасто, это не имеет значения. Но если это обычная операция, то вы можете использовать первый метод.
Я бы ожидал строку.Формат должен быть медленнее-он должен разобрать строку и затем объединить ее.
Пара заметок:
- формат - это способ поиска видимых пользователем строк в профессиональных приложениях; это позволяет избежать ошибок локализации
- Если вы заранее знаете длину результирующей строки, используйте конструктор StringBuilder (Int32) для определения емкости
Я думаю, что в большинстве случаев, таких как эта ясность, а не эффективность, должна быть вашей самой большой заботой. Если вы не дробите вместе тонны струн или не строите что-то для мобильного устройства с более низким энергопотреблением, это, вероятно, не сильно повлияет на вашу скорость бега.
Я обнаружил, что в случаях, когда я строю строки довольно линейным способом, либо делать прямые конкатенации, либо использовать StringBuilder-это ваш лучший вариант. Я предлагаю это в тех случаях, когда большинство строк то, что вы строите, динамично. Поскольку очень мало текста является статичным, самое главное, чтобы было ясно, где помещается каждый фрагмент динамического текста на случай, если он нуждается в обновлении в будущем.
С другой стороны, если вы говорите о большом куске статического текста с двумя или тремя переменными в нем, даже если он немного менее эффективен, я думаю, что ясность вы получаете от строки.Формат того стоит. Я использовал это ранее на этой неделе, когда мне нужно было разместить один бит динамического текст в центре 4-страничного документа. Это будет легче обновить этот большой кусок текста, если его в одной части, чем обновлять три части, которые вы объединяете вместе.
Хотя бы потому, что строка.Формат не совсем делает то, что вы могли бы подумать, вот повторный запуск тестов 6 лет спустя на Net45.
Конкат по-прежнему самый быстрый, но на самом деле разница составляет менее 30%. StringBuilder и формат отличаются всего на 5-10%. Я получил вариации 20%, запустив тесты несколько раз.Миллисекунды, миллион итераций:
Урок, который я выношу, заключается в том, что разница в производительности тривиальна, и поэтому она не должна мешать вам писать самый простой читаемый код, который вы можете. Что для моих денег часто, но не всегда
- конкатенация: 367
- новый stringBuilder для каждого ключа: 452
- Кэшированный StringBuilder: 419
- строка.Формат: 475
a + b + c
.const int iterations=1000000; var keyprefix= this.GetType().FullName; var maxkeylength=keyprefix + 1 + 1+ Math.Log10(iterations); Console.WriteLine("KeyPrefix \"{0}\", Max Key Length {1}",keyprefix, maxkeylength); var concatkeys= new string[iterations]; var stringbuilderkeys= new string[iterations]; var cachedsbkeys= new string[iterations]; var formatkeys= new string[iterations]; var stopwatch= new System.Diagnostics.Stopwatch(); Console.WriteLine("Concatenation:"); stopwatch.Start(); for(int i=0; i<iterations; i++){ var key1= keyprefix+":" + i.ToString(); concatkeys[i]=key1; } Console.WriteLine(stopwatch.ElapsedMilliseconds); Console.WriteLine("New stringBuilder for each key:"); stopwatch.Restart(); for(int i=0; i<iterations; i++){ var key2= new StringBuilder(keyprefix).Append(":").Append(i.ToString()).ToString(); stringbuilderkeys[i]= key2; } Console.WriteLine(stopwatch.ElapsedMilliseconds); Console.WriteLine("Cached StringBuilder:"); var cachedSB= new StringBuilder(maxkeylength); stopwatch.Restart(); for(int i=0; i<iterations; i++){ var key2b= cachedSB.Clear().Append(keyprefix).Append(":").Append(i.ToString()).ToString(); cachedsbkeys[i]= key2b; } Console.WriteLine(stopwatch.ElapsedMilliseconds); Console.WriteLine("string.Format"); stopwatch.Restart(); for(int i=0; i<iterations; i++){ var key3= string.Format("{0}:{1}", keyprefix,i.ToString()); formatkeys[i]= key3; } Console.WriteLine(stopwatch.ElapsedMilliseconds); var referToTheComputedValuesSoCompilerCantOptimiseTheLoopsAway= concatkeys.Union(stringbuilderkeys).Union(cachedsbkeys).Union(formatkeys).LastOrDefault(x=>x[1]=='-'); Console.WriteLine(referToTheComputedValuesSoCompilerCantOptimiseTheLoopsAway);
Строка.Формат использует
StringBuilder
internally...so логически это приводит к идее, что он будет немного менее производительным из-за больших накладных расходов. Однако простая конкатенация строк является самым быстрым методом введения одной строки между двумя others...by в значительной степени. Это доказательство было продемонстрировано Рико Мариани в его самом первом тесте производительности, много лет назад. Простой факт состоит в том, что конкатенации...когда известно количество струнных частей (без ограничений..вы могли бы объединить тысяча частей...так долго, как вы знаете, его всегда 1000 деталей)...всегда быстрее, чемStringBuilder
или строка.Формат. Они могут быть выполнены с одним выделением памяти и серией копий памяти. здесь является доказательствомИ вот фактический код для некоторой строки.Методы Concat, которые в конечном счете вызывают FillStringChecked, который использует указатели для копирования памяти (извлеченной через отражатель):
public static string Concat(params string[] values) { int totalLength = 0; if (values == null) { throw new ArgumentNullException("values"); } string[] strArray = new string[values.Length]; for (int i = 0; i < values.Length; i++) { string str = values[i]; strArray[i] = (str == null) ? Empty : str; totalLength += strArray[i].Length; if (totalLength < 0) { throw new OutOfMemoryException(); } } return ConcatArray(strArray, totalLength); } public static string Concat(string str0, string str1, string str2, string str3) { if (((str0 == null) && (str1 == null)) && ((str2 == null) && (str3 == null))) { return Empty; } if (str0 == null) { str0 = Empty; } if (str1 == null) { str1 = Empty; } if (str2 == null) { str2 = Empty; } if (str3 == null) { str3 = Empty; } int length = ((str0.Length + str1.Length) + str2.Length) + str3.Length; string dest = FastAllocateString(length); FillStringChecked(dest, 0, str0); FillStringChecked(dest, str0.Length, str1); FillStringChecked(dest, str0.Length + str1.Length, str2); FillStringChecked(dest, (str0.Length + str1.Length) + str2.Length, str3); return dest; } private static string ConcatArray(string[] values, int totalLength) { string dest = FastAllocateString(totalLength); int destPos = 0; for (int i = 0; i < values.Length; i++) { FillStringChecked(dest, destPos, values[i]); destPos += values[i].Length; } return dest; } private static unsafe void FillStringChecked(string dest, int destPos, string src) { int length = src.Length; if (length > (dest.Length - destPos)) { throw new IndexOutOfRangeException(); } fixed (char* chRef = &dest.m_firstChar) { fixed (char* chRef2 = &src.m_firstChar) { wstrcpy(chRef + destPos, chRef2, length); } } }
Итак:
string what = "cat"; string inthehat = "The " + what + " in the hat!";
Наслаждайтесь!
Это действительно зависит. Для небольших строк с небольшим количеством конкатенаций, это на самом деле быстрее просто добавить строки.
String s = "String A" + "String B";
Но для больших строк (очень-очень больших строк) более эффективно использовать StringBuilder.
В обоих случаях выше я хочу ввести одну или несколько строк в середину предопределенной строки шаблона.
В этом случае я бы предложил строку.Формат является самым быстрым, потому что он предназначен именно для этой цели.
Это действительно зависит от вашего шаблона использования.
Детальный ориентир междуstring.Join
,string,Concat
иstring.Format
можно найти здесь: строка.Формат не подходит для интенсивного ведения журнала