Fundamentally, if class-type fields (or variables, array slots, etc.) X
and Y
each hold a reference to a class object, there are two logical questions that (Object)X.Equals(Y)
can answer:
- If the reference in `Y` were copied to `X` (meaning the reference is copied), would the class have any reason to expect such a change to affect program semantics in any way (e.g. by affecting the present *or future* behavior of any members of `X` or `Y`)
- If *all* references to the target of `X` were instantaneously magically made to point to the target of `Y`, *and vice versa*`, should the class expect such a change to alter program behavior (e.g. by altering behavior of any member *other than an identity-based `GetHashCode`*, or by causing a storage location to refer to an object of incompatible type).
Note that if X
and Y
refer to objects of different types, neither function may legitimately return true unless both classes know that there cannot be any storage locations holding a reference to one which could not also hold a reference to the other [e.g. because both types are private classes derived from a common base, and neither is ever stored in any storage location (other than this
) whose type can't hold references to both].
The default Object.Equals
method answers the first question; ValueType.Equals
answers the second. The first question is generally the appropriate one to ask of object instances whose observable state may be mutated; the second is appropriate to ask of object instances whose observable state will not be mutated even if their types would allow it. If X
and Y
each hold a reference to a distinct int[1]
, and both arrays hold 23 in their first element, the first equality relation should define them as distinct [copying X
to Y
would alter the behavior of X[0]
if Y[0]
were modified], but the second should regard them as equivalent (swapping all references to the targets of X
and Y
wouldn't affect anything). Note that if the arrays held different values, the second test should regard the arrays as distinct, since swapping the objects would mean X[0]
would now report the value that Y[0]
used to report).
There's a pretty strong convention that mutable types (other than System.ValueType
and its descendants) should override Object.Equals
to implement the first type of equivalence relation; since it's impossible for System.ValueType
or its descendants to implement the first relation, they generally implement the second. Unfortunately, there's no standard convention by which objects which override Object.Equals()
for the first kind of relation should expose a method which tests for the second, even though an equivalence relation could be defined which allowed comparison between any two objects of any arbitrary type. The second relation would be useful in the standard pattern wherein an immutable class Imm
holds a private reference to a mutable type Mut
but doesn't expose that object to any code that could actually mutate it [making the instance immutable]. In such a case, there's no way for class Mut
to know that an instance will never be written, but it would be helpful to have a standard means by which two instances of Imm
could ask the Mut
s to which they hold references whether they would be equivalent if the holders of the references never mutated them. Note that the equivalence relation defined above makes no reference to mutation, nor to any particular means which Imm
must use to ensure that an instance won't be mutated, but its meaning is well-defined in any case. The object which holds a reference to Mut
should know whether that reference encapsulates identity, mutable state, or immutable state, and should thus be able to implement its own equality relation suitably.
IEqualityComparer(T)
and let the consumer choose which equality to use? – Dustin Kingen.Equals()
at all? – Matt SmithId
, then I would expectEquals
to compare only theId
fields. Anything else wouldn't make sense for theEquals
method, and would be potentially dangerous if you're comparing mutable fields. – Jim MischelEquals
could mean (and each of those meanings could be useful for a client). – Matt Smith