Подтвердить что ты не робот

Использует ли конкатенация строк внутри StringBuilder?

Три моих коллеги просто сказали мне, что нет причин использовать StringBuilder вместо конкатенации с помощью оператора +. Другими словами, это прекрасно связано с набором строк: myString1 + myString2 + myString3 + myString4 + mySt...

Обоснованием, которое они использовали, было то, что с .NET 2 компилятор С# будет строить тот же IL, если вы используете оператор +, как если бы вы использовали StringBuilder.

Это новость для меня. Правильны ли они?

4b9b3361

Ответ 1

Нет, они неверны. Конкатенация строк создает новый string, тогда как StringBuilder использует буфер переменной размера для создания строки, создавая объект string, когда < вызывается href= "http://msdn.microsoft.com/en-us/library/system.text.stringbuilder.tostring.aspx" rel= "noreferrer" > ToString().

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

Тем не менее, если вы объединяете постоянные строковые значения, операторы строк будут лучше, потому что компилятор будет их отделять, и если ваша исполняемая нециклическая конкатенация с использованием операторов будет лучше, поскольку они должны привести к один вызов string.Concat.

Ответ 2

Нет, они неправильны, это не приведет к тому же IL:

static string StringBuilder()
{
    var s1 = "s1";
    var s2 = "s2";
    var s3 = "s3";
    var s4 = "s4";
    var sb = new StringBuilder();
    sb.Append(s1).Append(s2).Append(s3).Append(s4);
    return sb.ToString();
}

static string Concat()
{
    var s1 = "s1";
    var s2 = "s2";
    var s3 = "s3";
    var s4 = "s4";
    return s1 + s2 + s3 + s4;
}

IL из StringBuilder:

.method private hidebysig static string StringBuilder() cil managed
{
    .maxstack 2
    .locals init (
        [0] string s1,
        [1] string s2,
        [2] string s3,
        [3] string s4,
        [4] class [mscorlib]System.Text.StringBuilder sb)
    L_0000: ldstr "s1"
    L_0005: stloc.0 
    L_0006: ldstr "s2"
    L_000b: stloc.1 
    L_000c: ldstr "s3"
    L_0011: stloc.2 
    L_0012: ldstr "s4"
    L_0017: stloc.3 
    L_0018: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor()
    L_001d: stloc.s sb
    L_001f: ldloc.s sb
    L_0021: ldloc.0 
    L_0022: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
    L_0027: ldloc.1 
    L_0028: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
    L_002d: ldloc.2 
    L_002e: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
    L_0033: ldloc.3 
    L_0034: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
    L_0039: pop 
    L_003a: ldloc.s sb
    L_003c: callvirt instance string [mscorlib]System.Object::ToString()
    L_0041: ret 
}

IL Конкат:

.method private hidebysig static string Concat() cil managed
{
    .maxstack 4
    .locals init (
        [0] string s1,
        [1] string s2,
        [2] string s3,
        [3] string s4)
    L_0000: ldstr "s1"
    L_0005: stloc.0 
    L_0006: ldstr "s2"
    L_000b: stloc.1 
    L_000c: ldstr "s3"
    L_0011: stloc.2 
    L_0012: ldstr "s4"
    L_0017: stloc.3 
    L_0018: ldloc.0 
    L_0019: ldloc.1 
    L_001a: ldloc.2 
    L_001b: ldloc.3 
    L_001c: call string [mscorlib]System.String::Concat(string, string, string, string)
    L_0021: ret 
}

Также вы можете найти эту статью.

Ответ 3

Нет, это не так. Они определенно генерируют разные ИЛ. Он использует разные вызовы: String.Concat в случае не StringBuilder.

String.Concat вызывает частный метод под названием ConcatArray, который выделяет новую строку, достаточно длинную, чтобы сохранить конечный результат. Таким образом, очень разные, но это не означает, что объединение с использованием оператора + менее эффективно, чем использование StringBuilder. Фактически, это почти наверняка более эффективно. Кроме того, в случае конкатенации констант это делается во время компиляции.

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

Ответ 4

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

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

Давайте построим некоторый тестовый код и посмотрим на него в Reflector, используя представление IL (или вы можете использовать ILDASM, в зависимости от того, что вы предпочитаете

Итак, сначала базовая линия - этот метод не конкатенируется вообще:


static void NoConcat()
{
  string test = "Hello World";
}

Теперь вот IL:


.method private hidebysig static void NoConcat() cil managed
{
    .maxstack 1
    .locals init (
        [0] string test)
    L_0000: nop 
    L_0001: ldstr "Hello World"  <----------NO reallocation!
    L_0006: stloc.0 
    L_0007: ret 
}

Хорошо, никаких сюрпризов, правильно?

Теперь давайте посмотрим на некоторый код, который определенно перераспределяет строку, поэтому мы знаем, как это выглядит:


static void Concat2()
{
  string test = "Hello";
  test += " ";
  test += "World";
}

Здесь IL, обратите внимание на перераспределение (он вызывает string.Concat, который вызывает выделение новой строки):


.method private hidebysig static void Concat2() cil managed
{
    .maxstack 2
    .locals init (
        [0] string test)
    L_0000: nop 
    L_0001: ldstr "Hello"
    L_0006: stloc.0 
    L_0007: ldloc.0 
    L_0008: ldstr " "
    L_000d: call string [mscorlib]System.String::Concat(string, string)
    L_0012: stloc.0 
    L_0013: ldloc.0 
    L_0014: ldstr "World"
    L_0019: call string [mscorlib]System.String::Concat(string, string)
    L_001e: stloc.0 
    L_001f: ret 
}

Итак, как насчет конкатенации, которая не вызывает перераспределения - мы собираемся объединить статические строки с оператором "+":


static void Concat1()
{
  string test = "Hello" + " " + "World";
}

Вот ИЛ - посмотри, какой умный компилятор! Он НЕ использует concat - он идентичен первому примеру:


.method private hidebysig static void Concat1() cil managed
{
    .maxstack 1
    .locals init (
        [0] string test)
    L_0000: nop 
    L_0001: ldstr "Hello World"
    L_0006: stloc.0 
    L_0007: ret 
}

Теперь давайте немного поиграем. Что делать, если мы смешиваем статические строки и переменные? (это то, где вам все-таки может быть лучше использовать stringbuilder)


static void Concat3(string text)
{
  string test = "Hello" + " " + text + " World";
}

И ИЛ. Обратите внимание, что он достаточно умен, чтобы объединить "Hello" и "" в качестве константы, но для текстовой переменной все равно нужно сделать concat:


.method private hidebysig static void Concat3(string text) cil managed
{
    .maxstack 3
    .locals init (
        [0] string test)
    L_0000: nop 
    L_0001: ldstr "Hello "
    L_0006: ldarg.0 
    L_0007: ldstr " World"
    L_000c: call string [mscorlib]System.String::Concat(string, string, string)
    L_0011: stloc.0 
    L_0012: ret 
}

Ответ 5

Я обычно придерживаюсь следующих правил:

  • Если число дочерних строк имеет foreknown, используйте конкатенацию. Это охватывает ситуацию, такую ​​как str1 + str2 + str3 +..., независимо от того, сколько они.

  • Если дочерние строки уже находятся в массиве, используйте string.join

  • Если вы строите строку в цикле, используйте StringBuilder

Ответ 6

Небольшое различие между String и StringBuilder:

Конкатенация строки приведет к созданию нового строкового объекта, являющегося результатом конкатенации. Конкатенация StringBuilder изменяет строковый объект.

Итак, они неверны.

Ответ 7

Там ОГРОМНАЯ разница в производительности между конкатенацией строк и StringBuidler. У нас был веб-сервис, который был слишком медленным. Мы изменили все струнные кошки на StringBuilder.Appends, и это стало намного быстрее!

Ответ 8

Нет, конкатенация строк не использует StringBuilder внутри. Однако в вашем конкретном примере нет преимущества для использования StringBuilder.

Это хорошо для нескольких строк (вы создаете только одну новую строку):

myString = myString + myString2 + myString3 + myString4 + mySt...

Это не (вы создаете и выделяете 4 строки и т.д.):

myString = myString + myString2;
myString = myString + myString3;
myString = myString + myString4;
myString = myString + myString5;

Из всех вопросов, касающихся stackoverflow об этом вопросе, у этого есть один из лучших ответов: String vs. StringBuilder

Ищите два ответа, один из которых - Джей Базузи, а тот - Джеймс Карран.

Кроме того, ВЫСОКО РЕКОМЕНДУЕТСЯ, Джефф Атвуд использует фактическое тестирование для сравнения этих и других сценариев конкатенации/построения строк здесь: http://www.codinghorror.com/blog/2009/01/the-sad-tragedy-of-micro-optimization-theater.html