Оператор перегрузки == versus Equals()
Я работаю над проектом С#, на котором до сих пор я использовал неизменяемые объекты и фабрики, чтобы гарантировать, что объекты типа Foo
всегда можно сравнить для равенства с ==
.
Foo
объекты не могут быть изменены после создания, а factory всегда возвращает один и тот же объект для заданного набора аргументов. Это отлично работает, и на всей базе кода мы предполагаем, что ==
всегда работает для проверки равенства.
Теперь мне нужно добавить некоторую функциональность, которая вводит кэш края, для которого это не всегда будет работать. Самое простое - перегрузить operator ==
для этого типа, так что ни один из других кодов в проекте не должен меняться. Но это поражает меня как запах кода: перегрузка operator ==
, а не Equals
кажется странной, и я привык к соглашению, что ==
проверяет ссылочное равенство, а Equals
проверяет равенство объекта (или что-то вроде термина есть).
Является ли это законной проблемой, или я должен просто идти вперед и перегружать operator ==
?
Ответы
Ответ 1
Я считаю, что стандартом является то, что для большинства типов .Equals проверяет сходство объектов, а оператор ==
проверяет ссылочное равенство.
Я считаю, что наилучшая практика заключается в том, что для неизменяемых типов оператор ==
должен проверять подобие, а также .Equals
. И если вы хотите знать, действительно ли они являются одним и тем же объектом, используйте .ReferenceEquals
. Для примера см. Класс С# String
.
Ответ 2
Существует большая разница между перегрузкой ==
и переопределением Равнов.
Если у вас есть выражение
if (x == y) {
Метод, который будет использоваться для сравнения переменных x и y, определяется с компиляцией. Это перегрузка оператора. Тип, используемый при объявлении x и y, используется для определения того, какой метод используется для их сравнения. Фактический тип внутри x и y (т.е. Подкласс или реализация интерфейса) не имеет значения. Рассмотрим следующее.
object x = "hello";
object y = 'h' + "ello"; // ensure it a different reference
if (x == y) { // evaluates to FALSE
и следующих
string x = "hello";
string y = 'h' + "ello"; // ensure it a different reference
if (x == y) { // evaluates to TRUE
Это демонстрирует, что тип, используемый для объявления переменных x и y, используется для определения того, какой метод используется для вычисления ==.
Для сравнения, Equals определяется в время выполнения на основе фактического типа внутри переменной x. Equals - это виртуальный метод Object, который другие типы могут и делают. Поэтому следующие два примера оцениваются как истинные.
object x = "hello";
object y = 'h' + "ello"; // ensure it a different reference
if (x.Equals(y)) { // evaluates to TRUE
и следующих
string x = "hello";
string y = 'h' + "ello"; // ensure it a different reference
if (x.Equals(y)) { // also evaluates to TRUE
Ответ 3
Для неизменяемых типов я не думаю, что есть что-то неправильное, если ==
перегружен для поддержки равенства ценности. Я не думаю, что переопределить ==
без переопределения Equals
, чтобы иметь ту же семантику. Если вы переопределите ==
и по какой-то причине вам нужно проверить ссылочное равенство, вы можете использовать Object.ReferenceEquals(a,b)
.
Смотрите статью Microsoft для некоторых полезных рекомендаций
Ответ 4
Это определенно пахнет. При перегрузке ==
вы должны убедиться, что оба Equals()
и GetHashCode()
также согласованы. См. рекомендации MSDN.
И единственная причина, по которой это вообще кажется ОК, заключается в том, что вы описываете свой тип как непреложный.
Ответ 5
Согласно самой лучшей практике Microsoft, результат метода Equals и перегрузки equals (==) должны быть одинаковыми.
CA2224: Переопределить равные для оператора перегрузки равно
Ответ 6
Пример, показывающий, как реализовать это в соответствии с рекомендациями MSFT (ниже). Обратите внимание: при переопределении Equals вам также необходимо переопределить GetHashCode(). Надеюсь, это поможет людям.
public class Person
{
public Guid Id { get; private set; }
public Person(Guid id)
{
Id = id;
}
public Person()
{
Id = System.Guid.NewGuid();
}
public static bool operator ==(Person p1, Person p2)
{
bool rc;
if (System.Object.ReferenceEquals(p1, p2))
{
rc = true;
}
else if (((object)p1 == null) || ((object)p2 == null))
{
rc = false;
}
else
{
rc = (p1.Id.CompareTo(p2.Id) == 0);
}
return rc;
}
public static bool operator !=(Person p1, Person p2)
{
return !(p1 == p2);
}
public override bool Equals(object obj)
{
bool rc = false;
if (obj is Person)
{
Person p2 = obj as Person;
rc = (this == p2);
}
return rc;
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
}