Сравнение струнных строк
Подойдя глубже в С#, я столкнулся с небольшой (странной) проблемой с равенством ссылки на объекты.
Пусть говорит, что у меня две строки:
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 поддерживает пул строк, поскольку они неизменяемы. Вы не должны заботиться об этом, так как он сам берет на себя повторное использование.