ReferenceEquals неправильно работает со строками

Почему в этой ситуации ReferenceEquals метод объекта ведет себя иначе?

string a= "fg";
string b= "fg";
Console.WriteLine(object.ReferenceEquals(a, b));

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

StringBuilder c = new StringBuilder("fg");
string d = c.ToString();
Console.WriteLine(object.ReferenceEquals(a, d));

В этом случае он работает нормально, и результат равен false, потому что он сравнивает ссылки на мои объекты.


person Chuck Norris    schedule 02.02.2012    source источник
comment
Даже Чак Норрис на SO. Ух ты!   -  person RBT    schedule 14.10.2016


Ответы (5)


В первом примере есть константа времени компиляции "fg", на которую ссылаются две переменные. Поскольку это константа времени компиляции, две переменные ссылаются на один объект. Ссылки равны.

Подробнее об этом поведении читайте в теме интернирования строк. Для начала рассмотрите:

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

http://msdn.microsoft.com/en-us/library/system.string.intern.aspx

Во втором примере только одно является константой времени компиляции, другое — результатом каких-то операций. a и d не ссылаются на один и тот же объект, поэтому вы получите ложный результат от ReferenceEquals.

person Anthony Pegram    schedule 02.02.2012
comment
@ Хенк, я не уверен, куда ты клонишь, хотя, признаюсь, я не совсем знаком с этой концепцией. Я просто читаю, что свертывание — это буквальное выражение, скажем, "f" + "g" будет преобразовано компилятором в "fg". Однако в коде string a = "fg"; string b = "fg"; вы ожидаете увидеть в выдаваемом IL IL_0000: ldstr "fg" / IL_0005: stloc.0 / IL_0006: ldstr "fg" / IL_000B: stloc.1 - person Anthony Pegram; 02.02.2012
comment
И действительно, если у вас также есть string c = "f" + "g", выдается та же инструкция для загрузки fg. Для моего собственного образования, где складывание для a и b? - person Anthony Pegram; 02.02.2012

В обоих случаях ведет себя корректно.

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

Обычно это происходит с каждой строковой константой в вашем приложении.

person Ben    schedule 02.02.2012

Поскольку вы ссылаетесь на один и тот же литерал ("fg"), обе ваши строки фактически будут указывать на одно и то же. Пожалуйста, ознакомьтесь с этой статьей: http://csharpindepth.com/Articles/General/Strings.aspx (пункт «Интернирование»).

С уважением, Петр

person Piotr Justyna    schedule 02.02.2012

Согласно этому публикации это имеет отношение к тому, что называется интернированием. a и b в вашем случае - это две переменные, указывающие на один и тот же экземпляр, поэтому ReferenceEquals возвращает true.

person granaker    schedule 02.02.2012

Это связано с тем, что в среде CLR версии выше 4.5 сборки отмечены атрибутом System.Runtime.CompilerServices.CompilationRelaxations Attribute, который определяет значение флага System.Runtime.CompilerServices. CompilationRelaxations.NoStringInterning. Это было реализовано с целью повышения производительности.

Если запустить ваш код в версии CLR ниже 4.5, переменные a и b будут ссылаться на разные строковые объекты в куче со значением fg, а object.ReferenceEquals(a, b) даст результат False.

Начиная с CLR 4.5 сравнение object.ReferenceEquals(a, b) даст результат True, потому что он интернирует строку fg при загрузке сборки в домен приложения. Это означает, что a и b ссылаются на одну и ту же строку в куче.

person Philip Voloshin    schedule 26.12.2020