Overloading just equality operators is not enough. Under different circumstances, all of the following can be called:
object.Equals
and object.GetHashCode
IEquatable<T>.Equals
(optional, allows avoiding boxing)operator ==
and operator !=
(optional, allows using operators)When overriding Equals
, GetHashCode
must also be overriden. When implementing Equals
, there are many special cases: comparing to objects of a different type, comparing to self etc.
When NOT overridden Equals
method and ==
operator behave differently for classes and structs. For classes just references are compared, and for structs values of properties are compared via reflection what can negatively affect performance. ==
can not be used for comparing structs unless it is overridden.
Generally equality operation must obey the following rules:
A
always equals A
(may not be true for NULL
values in some systems).A
equals B
, and B
equals C
, then A
equals C
.A
equals B
, then A
and B
have equal hash codes.B
and C
are instances of Class2
inherited from Class1
:
Class1.Equals(A,B)
must always return the same value as the call to Class2.Equals(A,B)
.class Student : IEquatable<Student>
{
public string Name { get; set; } = "";
public bool Equals(Student other)
{
if (ReferenceEquals(other, null)) return false;
if (ReferenceEquals(other, this)) return true;
return string.Equals(Name, other.Name);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
return Equals(obj as Student);
}
public override int GetHashCode()
{
return Name?.GetHashCode() ?? 0;
}
public static bool operator ==(Student left, Student right)
{
return Equals(left, right);
}
public static bool operator !=(Student left, Student right)
{
return !Equals(left, right);
}
}