Почему сравнение двух строк как объекта вызывает неожиданный результат

Рассмотрим следующий фрагмент кода.

object str = new string(new char[] { 't', 'e', 's', 't' });
object str1 = new string(new char[] { 't', 'e', 's', 't' });
Console.WriteLine(str==str1); // false
Console.WriteLine(str.Equals(str1));  // true

Я понимаю, что оператор равенства работает здесь, что, поскольку мы неявно бросаем объект, оператор равенства проверяет ссылки обоих, если они равны и возвращает false.

Но я запутался во втором, возвращая true, похоже, что он вызывает реализацию переопределения Equals, предоставляемую типом String, и проверяет содержимое строки, если они равны.

Мой вопрос в том, почему он не проверяет равномерность контента для оператора, а их фактический тип - это строка, а не объект. прав?

в то время как следующий код выводит для обоих:

object str = "test";
object str1 = "test";
Console.WriteLine(str==str1); // true
Console.WriteLine(str.Equals(str1)); // true

Ответы

Ответ 1

С

Console.WriteLine(str==str1); // false

определяется во время компиляции, которую С# предопределила (формальная) перегрузка operator == для использования. Поскольку str и str1 объявлены как object, выбирается перегрузка operator ==(object, object). Это фиксируется во время компиляции. Просто потому, что фактические типы времени выполнения более конкретны, это не меняется. Если вы хотите привязать во время выполнения, используйте Console.WriteLine((dynamic)str == (dynamic)str1); /* true */.

С

Console.WriteLine(str.Equals(str1));  // true

вы вызываете метод virtual на object. Виртуальный означает, что он перейдет к тому, что override имеет значение во время выполнения. Класс System.String имеет переопределение, и поскольку str будет иметь тип времени выполнения System.String, переопределение будет использоваться "виртуальной отправкой".


Относительно добавления к нижней части вашего вопроса: эта ситуация отличается из-за интернирования строк. String interning - это оптимизация, где один и тот же физический экземпляр используется для формально различных строк, значения которых идентичны. Когда у вас есть две строки, значения которых указаны в исходном коде, интернирование строк будет "оптимизировано" и сделать две ссылки на один и тот же экземпляр. Это обычно безвредно, потому что строки гарантированно неизменны. Поэтому, как правило, вам все равно, является ли он одним и тем же экземпляром или другим экземпляром с одинаковым значением. Но в вашем примере мы можем "раскрыть" интернирование.

Примечание. Интерпретация строк не имеет отношения к вашему исходному вопросу. Только после того, как вы добавили новый пример в свой вопрос, интернирование строк стало актуальным.

Ответ 2

Когда выражение == используется для выражения типа объекта, оно будет разрешено для System.Object.ReferenceEquals.

Equals - это просто виртуальный метод и ведет себя как таковой, поэтому будет использоваться переопределенная версия (которая для типа string сравнивает содержимое).

Ответ 3

Я считаю, что потому, что String == оператор принимает только String типы в качестве параметров, а .Equals < метод href= "http://referencesource.microsoft.com/#mscorlib/system/string.cs,592" rel= "nofollow" > принимает object типы как параметры.

Поскольку строка == принимает только типы String в качестве параметров, разрешение перегрузки выбирает оператор == для использования для сравнения.

Ответ 4

Это происходит из-за перевода строки; когда вы пишете:

object str = "test";
object str1 = "test";
Console.WriteLine(str==str1);

Это работает так, как если бы две строки были внутренне и тихо скопированы в одно место компилятором, поэтому два указателя на самом деле указывают на тот же объект.

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

Взгляните на эту статью: https://blogs.msdn.microsoft.com/ericlippert/2009/09/28/string-interning-and-string-empty/

Метод Equals переопределяется в строке, поэтому он сравнивает фактическое содержимое строки, а не адрес как == (ReferenceEquals) в вашем случае, поскольку тип объект.

Ответ 5

Помощь методу String.Equals дает это как замечание:

Этот метод выполняет порядковый номер (с учетом регистра и культура-нечувствительность).

Итак, сравнение выполняется путем проверки строки char на char, тем самым давая true.