Сколько объектов String будет создано при использовании знака "плюс"?

Сколько объектов String будет создано при использовании знака плюс в приведенном ниже коде?

String result = "1" + "2" + "3" + "4";

Если бы это было так, я бы сказал три объекта String: "1", "2", "12".

String result = "1" + "2";

Я также знаю, что объекты String кэшируются в String Intern Pool/Table для повышения производительности, но это не вопрос.

115
03 февр. '12 в 19:54
источник поделиться
6 ответов

Удивительно, но это зависит.

Если вы сделаете это в методе:

void Foo() {
    String one = "1";
    String two = "2";
    String result = one + two + "34";
    Console.Out.WriteLine(result);
}

то компилятор, кажется, испускает код с помощью String.Concat, как ответил @Joachim (+1 к нему кстати).

Если вы определяете их как константы, например:

const String one = "1";
const String two = "2";
const String result = one + two + "34";

или литералы, как в исходном вопросе:

String result = "1" + "2" + "3" + "4";

тогда компилятор будет оптимизировать эти знаки +. Это эквивалентно:

const String result = "1234";

Кроме того, компилятор удалит посторонние постоянные выражения и только испустит их, если они будут использованы или открыты. Например, эта программа:

const String one = "1";
const String two = "1";
const String result = one + two + "34";

public static void main(string[] args) {
    Console.Out.WriteLine(result);
}

Только генерирует одну строку - константу result (равную "1234" ). one и two не отображаются в полученном IL.

Имейте в виду, что во время выполнения могут быть дополнительные оптимизации. Я просто иду по тому, что ИЛ производится.

Наконец, что касается интернирования, то константы и литералы интернированы, но значение, которое интернировано, является конечным постоянным значением в IL, а не буквальным. Это означает, что вы можете получить меньшее количество строковых объектов, чем вы ожидаете, поскольку множество идентично определенных констант или литералов будут фактически одним и тем же объектом! Это иллюстрируется следующим:

public class Program
{
    private const String one = "1";
    private const String two = "2";
    private const String RESULT = one + two + "34";

    static String MakeIt()
    {
        return "1" + "2" + "3" + "4";
    }   

    static void Main(string[] args)
    {
        string result = "1" + "2" + "34";

        // Prints "True"
        Console.Out.WriteLine(Object.ReferenceEquals(result, MakeIt()));

        // Prints "True" also
        Console.Out.WriteLine(Object.ReferenceEquals(result, RESULT));
        Console.ReadKey();
    }
}

В случае, когда строки конкатенируются в цикле (или иначе динамически), вы получаете одну дополнительную строку для каждой конкатенации. Например, следующее создает 12 строковых экземпляров: 2 константы + 10 итераций, каждый из которых приводит к новому экземпляру String:

public class Program
{
    static void Main(string[] args)
    {
        string result = "";
        for (int i = 0; i < 10; i++)
            result += "a";
        Console.ReadKey();
    }
}

Но (также удивительно), несколько последовательных конкатенаций объединяются компилятором в одну конкатенацию нескольких строк. Например, эта программа также генерирует только 12 экземпляров строки! Это связано с тем, что " Даже если вы используете несколько операторов в одном из операторов, содержимое строки копируется только один раз."

public class Program
{
    static void Main(string[] args)
    {
        string result = "";
        for (int i = 0; i < 10; i++)
            result += "a" + result;
        Console.ReadKey();
    }
}
161
03 февр. '12 в 19:57
источник

Связанные вопросы


Похожие вопросы

Ответ Криса Шаина очень хорош. Как человек, который написал оптимизатор конкатенации строк, я бы просто добавил еще два интересных момента.

Во-первых, оптимизатор конкатенации по существу игнорирует как круглые скобки, так и левую ассоциативность, когда он может сделать это безопасно. Предположим, что у вас есть метод M(), который возвращает строку. Если вы скажете:

string s = M() + "A" + "B";

то компилятор объясняет, что оператор сложения оставлен ассоциативным, и поэтому это то же самое, что:

string s = ((M() + "A") + "B");

Но это:

string s = "C" + "D" + M();

совпадает с

string s = (("C" + "D") + M());

так что это конкатенация константной строки "CD" с помощью M().

Фактически, оптимизатор конкатенации понимает, что конкатенация строк ассоциативна и генерирует String.Concat(M(), "AB") для первого примера, хотя это и нарушает левую ассоциативность.

Вы можете даже сделать это:

string s = (M() + "E") + ("F" + M()));

и мы все равно будем генерировать String.Concat(M(), "EF", M()).

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

string s = (M() + "") + (null + M());

вы получите String.Concat(M(), M())

Возникает интересный вопрос: как насчет этого?

string s = M() + null;

Мы не можем оптимизировать это до

string s = M();

потому что M() может возвращать значение null, но String.Concat(M(), null) возвращает пустую строку, если M() возвращает значение null. Итак, что мы делаем, вместо этого уменьшаем

string s = M() + null;

to

string s = M() ?? "";

Таким образом, демонстрируя, что конкатенация строк вообще не требует вызова String.Concat.

Для дальнейшего чтения по этому вопросу см.

Почему String.Concat не оптимизирован для StringBuilder.Append?

86
04 февр. '12 в 0:48
источник

Я нашел ответ в MSDN. Один.

Как объединить несколько строк (руководство по программированию на С#)

Конкатенация - это процесс добавления одной строки в конец другая строка. Когда вы объединяете строковые литералы или строку константы с помощью оператора +, компилятор создает одиночный строка. Не происходит конкатенации времени выполнения. Однако строковые переменные могут быть объединены только во время выполнения. В этом случае вы должны понять последствия для различных подходов.

23
03 февр. '12 в 19:58
источник

Только один. Компилятор С# сбрасывает строковые константы и, следовательно, по существу компилируется до

String result = "1234";
22
03 февр. '12 в 19:58
источник

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

Если бы они были динамическими, они были бы оптимизированы для одного вызова String.Concat(строка, строка, строка, строка).

13
03 февр. '12 в 19:57
источник

Я сомневаюсь, что это предусмотрено любым стандартом или спецификацией. Одна версия может, вероятно, сделать что-то отличное от другого.

13
03 февр. '12 в 20:38
источник

Посмотрите другие вопросы по меткам или Задайте вопрос