Object Identity vs Equal Object Values
- Two references are identical if both refer to the same instance object.
- Two object instances can also be equal if the values that identify them are equal.
- Only reference types can be reference equal, thereby supporting the concept of identity.
- Calling
ReferenceEquals()
on value types will always return false since, by definition, the value type directly contains its data, not a reference.
- Even when
ReferenceEquals()
receives the same variable as parameters, the result will still be false because the very nature of value types is that they are copied into the parameters of the called method.
Check Equality by Value
- You can use the following methods to perform equality comparison:
object.Equals()
- The
==
operator.
ReferenceEquals()
, for explicitly checking reference equality.
- This approach is useful especially if
==
operator is overloaded.
- The call to
ReferenceEquals()
prevents us from making a recursive call to our own operator ==
overload.
public static bool operator == (Point first, Point second)
{
// return true if both first and second are same reference, or both null
if (ReferenceEquals(first, second)) return true;
// if either (but not both due to first check) is null, return false
if (ReferenceEquals(first, null) || ReferenceEquals(second, null)) return false;
// both not null, compare values
return first.X == second.X && first.Y == second.Y;
}
- For reference types, these three mechanisms are different:
- The
Equals()
method is an instance method of the System.Object
class.
- It does some checks for null, but then uses the == operator in the default implementation.
- The
==
operator resolves to the CIL ceq
instruction, which does a strict identity check.
- By overriding the
==
operator, a new method named op_Equality is defined/called.
- For built-in value types, the default behavior checks for value equality:
- The
Equals()
method performs a value equality check.
- The
==
operator resolves to the CIL ceq
instruction, which does a strict identity check.
- For user-defined structs:
- The
Equals()
method performs a value equality check by comparing each field of the struct (using reflection).
- You can (and should) override
Equals()
to provide an implementation specific to the struct.
- The
==
operator is undefined, unless you override it in the struct.
Overriding ToString()
- By default, calling
ToString()
on any object will return the fully qualified name of the class.
public class Coordinate
{
public string Longitude { get; set; }
public string Latitude { get; set; }
public override string ToString() => $"{Longitude} {Latitude}";
}
- Override the
GetHashCode()
when overriding Equals()
.
- There is a compiler warning to indicate this.
- Overriding
GetHashCode()
is a good practice when using it as a key into a hash table collection.
- The hash code's purpose is to efficiently balance a hash table by generating a number that corresponds to an object.
GetHashCode()
Implementation Principles
- Equal objects are required to have equal hash codes.