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

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

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, и он проверяет содержимое строки, если они равны.

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

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

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

5 ответов

Решение

С:

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

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

С:

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

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


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

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

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

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

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

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) в вашем случае, поскольку тип является объектом.

Я считаю, что это потому, что String== оператор только берет string типы в качестве параметров, в то время как .Equals метод занимает object типы в качестве параметров.

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

Помощь String.Equals Метод дает это в качестве замечания:

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

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

Другие вопросы по тегам