добавление строк в c #, как это делает компилятор? - PullRequest
6 голосов
/ 21 января 2011
A = string.Concat("abc","def") 

B = "abc" + "def"

A против B

В последнее время я был озадачен, почему многие скажут, что определенно А выполняет намного более быструю обработку по сравнению с Б. Но дело в том, что они просто скажут, потому что кто-то так сказал или потому, что это так. Я полагаю, я могу услышать гораздо лучшее объяснение отсюда.

Как компилятор обрабатывает эти строки?

Спасибо!

Ответы [ 6 ]

12 голосов
/ 21 января 2011

Самое первое, что я сделал, присоединившись к команде компилятора C #, - переписал оптимизатор для конкатенации строк. Хорошие времена.

Как уже отмечалось, конкататы строк из константных строк выполняются во время компиляции. Непостоянные строки делают некоторые причудливые вещи:

a + b --> String.Concat(a, b)
a + b + c --> String.Concat(a, b, c)
a + b + c + d --> String.Concat(a, b, c, d)
a + b + c + d + e --> String.Concat(new String[] { a, b, c, d, e })

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

Вот интересный. Предположим, у вас есть метод M, который возвращает строку:

s = M() + "";

Если M () возвращает ноль, то результатом является пустая строка. (null + empty пусто.) Если M не возвращает null, результат остается неизменным при конкатенации пустой строки. Следовательно, это на самом деле оптимизировано как совсем не вызов String.Concat! Становится

s = M() ?? ""

Аккуратно, а?

5 голосов
/ 21 января 2011
5 голосов
/ 21 января 2011

В C # оператор сложения для строк является просто синтаксическим сахаром для String.Concat. Вы можете убедиться в этом, открыв выходной узел в отражателе.

Еще одна вещь, на которую следует обратить внимание: если в вашем коде есть строковые литералы (или константы), например, в примере, компилятор даже изменит это на B = "abcdef".

Но если вы используете String.Concat с двумя строковыми литералами или константами, String.Concat будет по-прежнему вызываться, пропуская оптимизацию, и поэтому операция + будет на самом деле быстрее.

Итак, подведем итог:

stringA + stringB становится String.Concat(stringA, stringB).
"abc" + "def" становится "abcdef "
String.Concat("abc", "def") остается прежним

Что-то еще, что я просто должен был попробовать:

В C ++ / CLI "abc" + "def" + "ghi "фактически переводится в String.Concat(String.Concat("abc", "def"), "ghi")

1 голос
/ 21 января 2011

В данном конкретном случае два фактически идентичны. Компилятор преобразует второй вариант, использующий оператор +, в вызов Concat, первый вариант.

Ну, то есть, если две действительно содержат строковые переменные, которые были объединены.

Этот код:

B = "abc" + "def";

фактически превращается в это, без объединения вообще:

B = "abcdef";

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

Однако, если бы вы использовали что-то вроде этого:

A = String.Concat(stringVariable1, stringVariable2);
B = stringVariable1 + stringVariable2;

Тогда эти два сгенерируют один и тот же код.

Однако я хотел бы точно знать, что сказали эти «многие», так как я думаю, что это что-то другое.

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

Например, если вы сделаете это:

String s = "test";
for (int index = 1; index <= 10000; index++)
    s = s + "test";

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

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

Принимая во внимание следующий код:

StringBuilder sb = new StringBuilder("test");
for (int index = 1; index <= 10000; index++)
    sb.Append("test");

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

Таким образом, с точки зрения использования памяти и использования процессора, последний вариант намного лучше.

Кроме этого, я бы старался избегать чрезмерного сосредоточения на том, "является ли вариант кода X лучше Y", помимо того, что у вас уже есть. Например, сейчас я использую StringBuilder только потому, что знаю об этом деле, но это не означает, что весь код, который я пишу, использующий его, действительно нуждается в этом.

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

1 голос
/ 21 января 2011

Если строки являются литералами, как в вашем вопросе, то объединение строк, присвоенных B, будет выполнено во время компиляции.Ваш пример переводится в:

string a = string.Concat("abc", "def");
string b = "abcdef";

Если строки не являются литералами, то компилятор преобразует оператор + в вызов Concat.

Так вот ...1009 *

string x = GetStringFromSomewhere();
string y = GetAnotherString();

string a = string.Concat(x, y);
string b = x + y;

... переводится на это во время компиляции:

string x = GetStringFromSomewhere();
string y = GetAnotherString();

string a = string.Concat(x, y);
string b = string.Concat(x, y);
1 голос
/ 21 января 2011

На самом деле, B разрешается во время компиляции.В итоге вы получите B = "abcdef", тогда как для A конкатенация отложена до времени выполнения.

...