How does equality work in C# (==, Equals, ReferenceEquals, GetHashCode)?
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 overrideGetHashCode(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;Equalsis virtual and resolved at run time — they can diverge if you override one and not the other.- Value types get field-by-field
Equalsby default (via reflection — slow; override for perf). - Prefer a
recordwhen you want value equality and don't need custom logic.