Оператор переопределения ==. Как сравнить с null?
Возможный дубликат:
Как проверить нули в перегрузке оператора '== без бесконечной рекурсии?
Вероятно, это легкий ответ на этот вопрос... но, похоже, он ускользает от меня. Вот упрощенный пример:
public class Person
{
public string SocialSecurityNumber;
public string FirstName;
public string LastName;
}
Скажем, что для этого конкретного приложения справедливо сказать, что если числа социального обеспечения совпадают, и оба имени совпадают, то мы имеем в виду одного и того же "человека".
public override bool Equals(object Obj)
{
Person other = (Person)Obj;
return (this.SocialSecurityNumber == other.SocialSecurityNumber &&
this.FirstName == other.FirstName &&
this.LastName == other.LastName);
}
Чтобы все было согласовано, мы переопределяем и операторы == и!= для разработчиков в команде, которые не используют метод .Equals
.
public static bool operator !=(Person person1, Person person2)
{
return ! person1.Equals(person2);
}
public static bool operator ==(Person person1, Person person2)
{
return person1.Equals(person2);
}
Прекрасный и денди, верно?
Однако, что происходит, когда объект Person null
?
Вы не можете написать:
if (person == null)
{
//fail!
}
Так как это приведет к переопределению оператора ==, и код не будет выполнен:
person.Equals()
вызов метода, поскольку вы не можете вызвать метод для нулевого экземпляра.
С другой стороны, вы не можете явно проверить это условие внутри переопределения ==, так как это вызовет бесконечную рекурсию (и переполнение стека [dot com])
public static bool operator ==(Person person1, Person person2)
{
if (person1 == null)
{
//any code here never gets executed! We first die a slow painful death.
}
return person1.Equals(person2);
}
Итак, как вы переопределяете операторы == и!= для равенства значений и по-прежнему учитываете нулевые объекты?
Я надеюсь, что ответ не будет болезненно простым.: -)
Ответы
Ответ 1
Используйте object.ReferenceEquals(person1, null)
вместо оператора ==
:
public static bool operator ==(Person person1, Person person2)
{
if (object.ReferenceEquals(person1, null))
{
return object.ReferenceEquals(person2, null);
}
return person1.Equals(person2);
}
Ответ 2
вы всегда можете переопределить и поместить
(Object)(person1)==null
Я бы предположил, что это сработает, но не уверен.
Ответ 3
Я всегда делал это так (для операторов == и! =), и я повторно использую этот код для каждого создаваемого объекта:
public static bool operator ==(Person lhs, Person rhs)
{
// If left hand side is null...
if (System.Object.ReferenceEquals(lhs, null))
{
// ...and right hand side is null...
if (System.Object.ReferenceEquals(rhs, null))
{
//...both are null and are Equal.
return true;
}
// ...right hand side is not null, therefore not Equal.
return false;
}
// Return true if the fields match:
return lhs.Equals(rhs);
}
"! =", то происходит следующим образом:
public static bool operator !=(Person lhs, Person rhs)
{
return !(lhs == rhs);
}
Изменить
Я изменил операционную функцию ==
в соответствии с предлагаемой Microsoft версией здесь.
Ответ 4
Вставьте экземпляр Person
в object
:
public static bool operator ==(Person person1, Person person2)
{
if ((object)person1 == (object)person2) return true;
if ((object)person1 == null) return false;
if ((object)person2 == null) return false;
return person1.Equals(person2);
}
Ответ 5
Перегрузка этих операторов последовательно довольно сложная. Мой ответ на соответствующий вопрос может служить шаблоном.
В принципе, вам сначала нужно выполнить ссылку (object.ReferenceEquals
), чтобы проверить, есть ли объект null
. Затем вы вызываете Equals
.
Ответ 6
Перенесите Person в объект и затем выполните сравнение:
object o1 = (object)person1;
object o2 = (object)person2;
if(o1==o2) //compare instances.
return true;
if (o1 == null || o2 == null) //compare to null.
return false;
//continue with Person logic.
Ответ 7
Последняя (гипотетическая) процедура приведена ниже. Он очень похож на первый принятый ответ @cdhowie.
public static bool operator ==(Person person1, Person person2)
{
if (Person.ReferenceEquals(person1, person2)) return true;
if (Person.ReferenceEquals(person1, null)) return false; //*
return person1.Equals(person2);
}
Спасибо за отличные ответы!
//* - .Equals()
выполняет нулевую проверку на person2
Ответ 8
cdhowie находится на деньгах с использованием ReferenceEquals
, но стоит заметить, что вы все равно можете получить исключение, если кто-то передает null
непосредственно на Equals
. Кроме того, если вы собираетесь переопределить Equals
, почти всегда стоит реализовать IEquatable<T>
, поэтому я бы вместо этого имел.
public class Person : IEquatable<Person>
{
/* more stuff elided */
public bool Equals(Person other)
{
return !ReferenceEquals(other, null) &&
SocialSecurityNumber == other.SocialSecurityNumber &&
FirstName == other.FirstName &&
LastName == other.LastName;
}
public override bool Equals(object obj)
{
return Equals(obj as Person);
}
public static bool operator !=(Person person1, Person person2)
{
return !(person1 == person2);
}
public static bool operator ==(Person person1, Person person2)
{
return ReferenceEquals(person1, person2)
|| (!ReferenceEquals(person1, null) && person1.Equals(person2));
}
}
И, конечно, вы никогда не должны переопределять Equals
и не переопределять GetHashCode()
public override int GetHashCode()
{
//I'm going to assume that different
//people with the same SocialSecurityNumber are extremely rare,
//as optimise by hashing on that alone. If this isn't the case, change this
return SocialSecurityNumber.GetHashCode();
}
Также стоит отметить, что идентичность влечет равенство (то есть для любого действительного понятия "равенства" что-то всегда равно себе). Поскольку тесты на равенство могут быть дорогими и происходят в циклах, и поскольку сравнение чего-то с самим собой, как правило, довольно распространено в реальном коде (например, если объекты передаются в нескольких местах), его можно добавить как ярлык:
public bool Equals(Person other)
{
return !ReferenceEquals(other, null) &&
ReferenceEquals(this, other) ||
(
SocialSecurityNumber == other.SocialSecurityNumber &&
FirstName == other.FirstName &&
LastName == other.LastName
);
}
Насколько значительная часть сокращения на ReferenceEquals(this, other)
может значительно варьироваться в зависимости от характера класса, но стоит ли это делать или нет, это то, что нужно всегда учитывать, поэтому я включаю здесь технику.
Ответ 9
Легче любого из этих подходов было бы просто использовать
public static bool operator ==(Person person1, Person person2)
{
EqualityComparer<Person>.Default.Equals(person1, person2)
}
Это имеет ту же нулевую семантику равенства, что и подходы, которые предлагают все остальные, но это проблема структуры, чтобы выяснить детали:)