How does equality work in C# (==, Equals, ReferenceEquals, GetHashCode)?

C# OOP Intermediate 2–3 Yrs3–5 Yrs Conceptual 1 min read

Quick Answer

For reference types, == and Equals default to reference equality (same object). Override Equals and GetHashCode together (and implement IEquatable<T>) for value semantics. ReferenceEquals always compares references regardless of overrides. string overrides == for value comparison; record types do it all for you.

By default, for a class, both == and Equals check whether it's literally the same object in memory. If I want two objects with the same values to be considered equal, I override Equals — and I must override GetHashCode at the same time, based on the same fields, or dictionaries and hash sets stop working. ReferenceEquals ignores any overrides and always compares identity. One subtlety: == is static and bound at compile time while Equals is virtual, so if you override one and not the other they can disagree. Records handle all of this automatically.

  • Rules when overriding Equals: also override GetHashCode (equal objects must have equal hashes, or dictionaries/sets break), keep them consistent, and base the hash on the same fields used for equality (ideally immutable fields).
  • == is static and resolved at compile time; Equals is virtual and resolved at run time — they can diverge if you override one and not the other.
  • Value types get field-by-field Equals by default (via reflection — slow; override for perf).
  • Prefer a record when you want value equality and don't need custom logic.