There are two ways of checking whether a pair of objects are equal: reference equality and structural/value equality. Reference equality is the default for all reference types (classes), and structural equality is the default for all value types (but the default implementation is not optimal). Use the following guide to implement structural equality for both reference types and value types.
Equality
The equality check should follow these rules:
- An object is equal to itself (Identity)
- Comparing
x
to y
returns the same truth as comparing y
to x
. (Symmetry)
- If
x
is equal to y
and y
is equal to z
, then x
must be equal to z
. (Transitivity)
- An object is never equal to
null
.
null
is equal to null
.
- No exceptions should be thrown.
Let your class or struct implement the IEquatable<T>
interface for custom equality checking,
then implement the IEquatable<T>.Equals(T)
and Object.Equals()
methods.
Equality for reference types (classes)
For reference types, implement the IEquatable<T>.Equals(T)
method like this:
public bool Equals(MyType other)
{
if (Object.ReferenceEquals(other, null) || // When 'other' is null
other.GetType() != this.GetType()) // or of a different type
return false; // they are not equal.
return this.field1 == other.field1
&& this.field2 == other.field2;
}
Then override Object.Equals()
like this:
public override bool Equals(object obj)
{
return Equals(obj as MyType);
}
Equality for value types (structs)
Since value types cannot be null
, implement the IEquatable<T>.Equals(T)
method like this:
public bool Equals(MyType other)
{
return this.field == other.field
&& this.field2 == other.field2;
}
Then override Object.Equals()
like this:
public override bool Equals(object obj)
{
if (!(obj is MyType))
return false;
return Equals((MyType)obj);
}
Equality operators
For both reference and value types, you may want to override the default equality and inequality operators. Based on this post by Jon Skeet the equality and inequality operators can be implemented like this:
public static bool operator ==(MyType left, MyType right)
{
return Object.Equals(left, right);
}
public static bool operator !=(MyType left, MyType right)
{
return !(left == right);
}
Note that when left
and/or right
is null
, Object.Equals(object, object)
does not call the Object.Equals(object)
override (and therefore not the IEquatable<T>.Equals(T)
method).
Hash code
Sometimes the hash code of an object is important, for example when the object might be put in a dictionary or hash table. In general, when you override the Equals()
method, override the GetHashCode()
method. The hash code should follow these rules:
- The hash code should never change, not even after modifying some fields in the object.
- The hash code must be equal, for objects that are considered to be equal.
- The hash code may be anything (including equal) for objects that are considered to be unequal.
- The hash codes should be randomly distributed.
- The hash code function must never throw an exception and must always return.
- The hash code should be calculated very fast.
So to implement Object.GetHashCode()
for a class or struct that uses structural equality, choose some fields from your object that are immutable and mark them readonly
. Use only those fields to calculate the hash code. Override the Object.GetHashCode()
method and implement it like this:
public override int GetHashCode()
{
unchecked
{
int hash = 17;
// Don't forget to check for null values.
hash = hash * 29 + field1.GetHashCode();
hash = hash * 29 + field2.GetHashCode();
// ...
return hash;
}
}
Or, if you have only one immutable field, you may consider just using:
public override int GetHashCode()
{
// Don't forget to check for null values.
return field1.GetHashCode();
}
If you have no immutable fields, return a constant hash code. For example, the hash code of the type itself.
public override int GetHashCode()
{
return GetType().GetHashCode();
}
Structural equality for collections
Collections should not be compared using the default Equals()
method. Instead, the default equality for collections should be reference equality. To also implement structural equality, implement the IStructuralEquatable
interface. For example:
bool IStructuralEquatable.Equals(object obj, IEqualityComparer comparer)
{
var other = obj as MyType;
if (other == null)
return false;
return ((IStructuralEquatable)this.innerArray)
.Equals(other.innerArray, comparer);
}
int IStructuralEquatable.GetHashCode(IEqualityComparer comparer)
{
return ((IStructuralEquatable)this.innerArray).GetHashCode(comparer);
}