Two objects can contain the same values without being the same object. Reference equality asks whether two references identify one object. That distinction matters when an equality operator does more work than a quick identity check: a C# record class generates value equality, so comparing two linked lists with `==` can recursively inspect their contents and may overflow the call stack on long lists. section 1.2.1
Identity becomes especially useful when construction guarantees a unique object for each value. Memoized quadtree factories deduplicate equal grids, including empty grids. With that guarantee, `this == Empty(Level)` can recognize an empty quad by comparing references rather than traversing its cells. The quick check depends on the factory invariant; reference equality alone does not establish that separately created objects have equal contents. section 4.3.5
Node equivalence without recursive edge comparisons
A directed acyclic word graph node comparer first checks `ReferenceEquals(x, y)`, then handles nulls. For distinct nodes, it compares end-of-word status, outgoing-edge counts and labels, and uses `ReferenceEquals(x.FollowEdge(e), y.FollowEdge(e))` on corresponding destinations. Thus node equivalence depends on identical destination objects, not a fresh recursive comparison of their contents. Because the comparer also supplies a hash code, mutating a stored node in a way that changes that code can make it unfindable in a hash set or dictionary. section 5.4.2
Listing 5.6 Computing Node equivalence with an equality comparer
sealed class DawgBuilder
{
private class NodeEquality : IEqualityComparer<Node>
{
public bool Equals(Node? x, Node? y)
{
if (ReferenceEquals(x, y))
return true;
if (ReferenceEquals(x, null) || ReferenceEquals(y, null))
return false;
if (x.IsEnd != y.IsEnd || x.Count != y.Count)
return false;
return x.EdgeLabels.All(e =>
y.HasEdge(e) &&
ReferenceEquals(x.FollowEdge(e), y.FollowEdge(e)));
}
The C# comparer shows the identity fast path and the reference checks on outgoing edges. section 5.4.2
The trade-off is that identity may stop answering the question you need. A deferred `Pop` on a Hughes list could be cheap, but an empty result would be hidden inside a delegate; `IsEmpty` could no longer use a reference comparison and would need to construct the resulting stack. Conversely, memoized factories for immutable unification terms could make equality comparisons fast reference checks while deduplicating symbols. Choose reference equality when the representation makes identity meaningful, not merely because it is cheap. section 2.7.6 section 10.2



