Оптимизируются ли строковые литералы компилятором?

Компилятор C # или .NET CLR выполняет какую-либо умную оптимизацию памяти строковых литералов / констант? Могу поклясться, что слышал о концепции «интернализации строк», так что в любых двух битах кода в программе литерал «это строка» на самом деле относился бы к одному и тому же объекту (предположительно безопасному, что со строками неизменный?). Однако я не могу найти полезной ссылки на него в Google ...

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


person Neil Barnwell    schedule 26.11.2010    source источник
comment
Связанный stackoverflow.com/q/372547/38206   -  person Brian Rasmussen    schedule 26.11.2010


Ответы (3)


РЕДАКТИРОВАТЬ: Хотя я сильно подозреваю, что приведенное ниже утверждение верно для всех реализаций компилятора C #, я не уверен, что это действительно гарантировано в спецификации. В разделе 2.4.4.5 спецификации говорится о литералах, относящихся к одному и тому же экземпляру строки, но не упоминаются другие константные строковые выражения. Я подозреваю, что это упущение в спецификации - я напишу Мэдсу и Эрику об этом по электронной почте.


Это не просто строковые литералы. Это любая строка константа. Так, например, рассмотрим:

public const string X = "X";
public const string Y = "Y";
public const string XY = "XY";

void Foo()
{
    string z = X + Y;
}

Компилятор понимает, что здесь конкатенация (для z) происходит между двумя константными строками, и поэтому результат также является константной строкой. Следовательно, начальное значение z будет той же ссылкой, что и значение XY, потому что они константы времени компиляции с тем же значением.

РЕДАКТИРОВАТЬ: ответ Мэдса и Эрика предположил, что в компиляторе Microsoft C # строковые константы и строковые литералы обычно обрабатываются одинаково, но другие реализации могут отличаться.

person Jon Skeet    schedule 26.11.2010
comment
Указывают ли две одинаковые строковые константы в разных сборках на один и тот же объект? / Имеет ли джиттер внутренние строковые литералы? - person CodesInChaos; 26.11.2010
comment
@CodeInChaos: я считаю, что это зависит от атрибута CompilationRelaxationsAttribute(CompilationRelaxations.NoStringInterning). Хотя я бы не хотел сказать наверняка. - person Jon Skeet; 26.11.2010
comment
Привет, @JonSkeet, посоветуйте, пожалуйста, всегда ли у интернированных строк с одинаковым содержимым одинаковая ссылка? Означает ли это, что сравнение ссылок на такие строки вернет истину? - person Johnny_D; 24.04.2013
comment
@Johnny_D: Да и да - по крайней мере, гарантировано в рамках одной сборки. Между сборками становится сложнее, IIRC. - person Jon Skeet; 24.04.2013

В этой статье довольно хорошо объясняется интернирование строк. Цитировать:

В .NET существует концепция «внутреннего пула». По сути, это просто набор строк, но он гарантирует, что каждый раз, когда вы ссылаетесь на один и тот же строковый литерал, вы получаете ссылку на одну и ту же строку. Это, вероятно, зависит от языка, но это, безусловно, верно для C # и VB.NET, и я был бы очень удивлен, если бы увидел язык, для которого он не поддерживался, поскольку IL делает это очень легко (вероятно, проще, чем не внутренние литералы). Помимо автоматического интернирования литералов, вы можете вручную интернировать строки с помощью метода Intern и проверять, существует ли уже интернированная строка с такой же последовательностью символов в пуле, используя метод IsInterned. Это несколько неинтуитивно возвращает строку, а не логическое значение - если в пуле есть такая же строка, возвращается ссылка на эту строку. В противном случае возвращается null. Аналогичным образом, метод Intern возвращает ссылку на интернированную строку - либо строку, которую вы передали, если она уже была в пуле, либо вновь созданную интернированную строку, либо такую ​​же строку, которая уже была в пуле.

person Darin Dimitrov    schedule 26.11.2010
comment
Замечание: поскольку внутренние строки не освобождаются во время работы AppDomain, неправильное использование промежуточных запросов может вызвать утечку памяти. - person CodesInChaos; 26.11.2010

Да, он оптимизирует строковые литералы. Один простой пример, где вы можете это увидеть:

string s1="A";
string s2="A";
object.ReferenceEquals(s1,s2);  //true
person CodesInChaos    schedule 26.11.2010