Разница между оператором == и методом Equals() в С#?
В чем разница между ==
и Equals()
с примером? Я знаю, что ==
используется для сравнения оператора, а метод Equals()
используется для сравнения содержимого строки. Так я пробовал
// first example
string s1 = "a";
string s2 = "a";
Console.Write(a.Equals(s2)); // returns true, but if I assign "b" to s2,
// then result will be false
// second example
string s1 ="a";
string s2 ="a";
Console.Write(s1 == s2); // returns true
Как это так? Оба - разные ссылки на объекты. Предположим, мы считаем, что это ссылки. Но я старался использовать это как
string s1 = new string("ab");
string s2 = new string("ab");
Я получаю ошибку времени компиляции, которая не может преобразовать строку в char
Ответы
Ответ 1
Происходит несколько вещей. Во-первых, в этом примере:
string s1 = "a";
string s2 = "a";
Console.WriteLine(s1 == s2);
Вы утверждаете, что:
Оба являются разными объектами.
Это неверно из-за интернирования строк. s1
и s2
являются ссылками на один и тот же объект. Спецификация С# гарантирует, что - из раздела 2.4.4.5 спецификации С# 4:
Когда два или более строковых литерала, эквивалентные в соответствии с оператором равенства строк (§7.10.7), отображаются в одной и той же программе, эти строковые литералы относятся к одному и тому же экземпляру строки.
Итак, в этом конкретном случае вы все равно получите "true", даже если вы напечатали object.ReferenceEquals(s1, s2)
, или если вы использовали его для сравнения подлинности ссылки с ==
:
object s1 = "a";
object s2 = "a";
Console.WriteLine(s1 == s2); // Still prints True due to string literal interning
Однако, даже если это ссылки на отдельные объекты, ==
перегружается для string
. Перегрузка - это решение, используемое во время компиляции. Реализация для использования зависит от типов экземпляров для компиляции. Так, например:
string a = new string('x', 1);
string b = new string('x', 1);
Console.WriteLine(a == b); // Uses string implementation, prints True
object c = a;
object d = b;
Console.WriteLine(c == d); // Reference identity comparison, prints False
Сравните это с object.Equals(object)
, который является виртуальным методом. Как это бывает, string
также перегружает этот метод, но, тем не менее, он переопределяет его. Поэтому, если мы изменим наш код на:
string a = new string('x', 1);
string b = new string('x', 1);
Console.WriteLine(a.Equals((object) b));
object c = a;
object d = b;
Console.WriteLine(c.Equals(d));
... тогда оба вызова метода в скомпилированном коде просто будут object.Equals(object)
, но они все равно оба будут печатать True из-за полиморфизма: будет использоваться реализация в string
.
Вот как выглядит вызов перегруженного метода:
string a = new string('x', 1);
string b = new string('x', 1);
Console.WriteLine(a.Equals(b)); // Calls string.Equals(string)
Ответ 2
Цитата из документации Равные:
Стандартная реализация Equals поддерживает ссылочное равенство для ссылочные типы и побитовое равенство для типов значений. Справка равенство означает, что ссылки на объекты, которые сравниваются, относятся к тот же объект. Поразрядное равенство означает, что объекты, которые сравниваются, то же двоичное представление.
И оператор ==:
Для предопределенных типов значений оператор равенства (==) возвращает true, если значения его операндов равны, в противном случае - false. Для справки типы, отличные от string, == возвращает true, если его два операнда ссылаются на тот же объект. Для типа строки == сравнивает значения строки.
Теперь вернемся к вашему вопросу: почему s1 == s2
возвращает true? Строки - это специальные животные в .NET. Они представляют собой неизменяемые ссылочные типы. Они интернированы в .NET. Это означает, что если у вас есть две строковые константы с одинаковым значением, они будут ссылаться на один и тот же экземпляр объекта во время выполнения.
Цитата из документации:
Общая среда выполнения в режиме реального времени сохраняет хранилище строк, поддерживая table, называемый междоменным пулом, который содержит единственную ссылку на каждая уникальная литеральная строка, объявленная или созданная программно в вашей программы. Следовательно, экземпляр литеральной строки с конкретное значение существует только один раз в системе.
Ответ 3
Вы думаете, что Java-esque. В java оператор ==
не может быть настроен, поэтому для ссылочных типов он всегда означает ссылочное равенство, тогда как это означает равенство значений для примитивных типов. С другой стороны, Equals
предназначен для проверки равенства значений в ссылочных типах.
Тем не менее, в С# все по-другому. Оба Equals
и ==
могут иметь пользовательские реализации. Разница в том, что Equals
- это виртуальный (экземпляр) метод, а operator==
- статический метод. Кроме того, они могут вести себя точно так же.
По умолчанию оба Equals
и ==
проверяют ссылочное равенство для ссылочных типов и равенство значений для типов значений. Однако для string
обе настроены для проверки равенства значений.