Сравнение струнных строк

Подойдя глубже в С#, я столкнулся с небольшой (странной) проблемой с равенством ссылки на объекты. Пусть говорит, что у меня две строки:

String a = "Hello world!";
String b = "Bonjour le monde";
bool equals = ReferenceEquals(a, b);  // ******************* (1)
b = "Hello world!";
equals = ReferenceEquals(a, b);       // ******************* (2)

(1) Является false, и это ожидается. ReferenceEquals Документация говорит

ReferenceEquals сравнивает экземпляры

но затем:

  • Почему (2) возвращает true?
  • Строки a и b - это не тот же объект? Если да, то как они стали теми же, что я никогда не явно сделал a=b

Ответ 1

Это из-за интернирования строк.

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

Например, если вы назначаете одну и ту же литеральную строку нескольким переменные, среда выполнения возвращает ту же ссылку на литерал string из внутреннего пула и присваивает его каждой переменной.

Ответ 2

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

Ваш код компилируется в инструкции CIL, похожие на следующие:

IL_0001: ldstr "Hello world!"
IL_0006: stloc.0
IL_0007: ldstr "Bonjour le monde"
IL_000c: stloc.1
etc...

Из документации инструкции ldstr ( "загрузить буквальную строку" ) в спецификацию ECMA:

По умолчанию CLI гарантирует, что результат двух команд ldstr ссылается на два токена метаданных, которые имеют одинаковую последовательность символов, возвращают точно один и тот же строковый объект (процесс, известный как "строка" интернирование "). Такое поведение можно контролировать с помощью System.Runtime.CompilerServices.CompilationRelaxationsAttribute и System.Runtime.CompilerServices.CompilationRelaxations.NoStringInterning.

Вы также можете сами ставить строки, вызывая метод String.Intern.

Ответ 3

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

Взято из документы Microsoft:

Каждый строковый литерал не обязательно приводит к новой строке пример. Когда два или более строковых литерала, которые эквивалентны в соответствии с оператором равенства строк (раздел 7.9.7), появляются в одна и та же сборка, эти строковые литералы относятся к одной и той же строке пример. Например, вывод, созданный

class Test
{
   static void Main() {
      object a = "hello";
      object b = "hello";
      System.Console.WriteLine(a == b);
   }
}

Истинно, потому что два литерала относятся к одному экземпляру строки.

Ответ 4

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