Оператор перегрузки == против Equals()
Я работаю над проектом C#, в котором до сих пор я использовал неизменяемые объекты и фабрики, чтобы гарантировать, что объекты типа Foo
всегда можно сравнить на равенство с ==
,
Foo
объекты не могут быть изменены после создания, и фабрика всегда возвращает один и тот же объект для заданного набора аргументов. Это прекрасно работает, и во всей кодовой базе мы предполагаем, что ==
всегда работает для проверки равенства.
Теперь мне нужно добавить некоторые функциональные возможности, которые вводят крайний случай, для которого это не всегда работает. Самое простое, что нужно сделать, это перегрузить operator ==
для этого типа, так что ни один другой код в проекте не должен изменяться. Но это кажется мне запахом кода: перегрузка operator ==
и не Equals
просто кажется странным, и я привык к соглашению, что ==
проверяет равенство ссылок и Equals
проверяет равенство объектов (или любой другой термин).
Это законное беспокойство, или я должен просто пойти дальше и перегрузить operator ==
?
6 ответов
Я считаю, что стандартом является то, что для большинства типов.Equals проверяет сходство объектов и оператор ==
проверяет равенство ссылок.
Я считаю, что лучшая практика заключается в том, что для неизменяемых типов оператор ==
следует проверять на сходство, а также .Equals
, И если вы хотите узнать, действительно ли это один и тот же объект, используйте .ReferenceEquals
, Смотрите C# String
класс для примера этого.
Существует большая разница между перегрузкой ==
и переопределение равно.
Когда у вас есть выражение
if (x == y) {
Метод, который будет использоваться для сравнения переменных x и y, определяется во время компиляции. Это перегрузка оператора. Тип, используемый при объявлении x и y, используется для определения того, какой метод используется для их сравнения. Фактический тип в пределах x и y (то есть, реализация подкласса или интерфейса) не имеет значения. Учтите следующее.
object x = "hello";
object y = 'h' + "ello"; // ensure it's a different reference
if (x == y) { // evaluates to FALSE
и следующее
string x = "hello";
string y = 'h' + "ello"; // ensure it's a different reference
if (x == y) { // evaluates to TRUE
Это показывает, что тип, используемый для объявления переменных x и y, используется для определения того, какой метод используется для оценки ==.
Для сравнения, Equals определяется во время выполнения на основе фактического типа в переменной x. Equals - это виртуальный метод объекта, который другие типы могут и могут переопределять. Поэтому оба следующих примера оценивают как истинные.
object x = "hello";
object y = 'h' + "ello"; // ensure it's a different reference
if (x.Equals(y)) { // evaluates to TRUE
и следующее
string x = "hello";
string y = 'h' + "ello"; // ensure it's a different reference
if (x.Equals(y)) { // also evaluates to TRUE
Это определенно пахнет. При перегрузке ==
Вы должны убедиться, что оба Equals()
а также GetHashCode()
также последовательны. См. Рекомендации MSDN.
И единственная причина, по которой это кажется нормальным - это то, что вы описываете свой тип как неизменяемый.
Пример, показывающий, как реализовать это в соответствии с рекомендациями 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();
}
}
Для неизменяемых типов я не думаю, что что-то не так с ==
перегружен для поддержки равенства стоимости. Я не думаю, что я переопределил бы ==
без переопределения Equals
иметь ту же семантику, однако. Если вы переопределите ==
и нужно проверить равенство ссылок по какой-то причине, вы можете использовать Object.ReferenceEquals(a,b)
,
Смотрите эту статью Microsoft для некоторых полезных рекомендаций
Согласно собственным рекомендациям Microsoft, результат метода Equals и перегрузки equals (==) должны быть одинаковыми.
CA2224: переопределение равно при перегрузке оператора равно