Non Readonly In GetHashCode
ID |
csharp.non_readonly_in_gethashcode |
Severity |
low |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
CSharp |
Tags |
hashing, reliability |
Description
Reports an override of GetHashCode() that mixes a mutable instance field into the hash.
A hash code is only meaningful while the object’s identity-relevant state cannot change;
folding a non-readonly (and non-const) field into it breaks the invariant of every
hash-based collection that has already stored the object.
Rationale
Dictionary, HashSet, Hashtable and LINQ GroupBy/Distinct decide which bucket an
object belongs in by calling GetHashCode() once, at insertion time. If a field that
contributes to the hash is later reassigned, the object’s stored bucket no longer matches
its current hash — Contains returns false, Remove fails silently, and enumeration
can return duplicates. Making the contributing field readonly (or const) eliminates
the failure mode at the source.
public class Point
{
public int X; // mutable
public readonly int Y; // immutable
public override int GetHashCode()
{
return X.GetHashCode() ^ // FLAW — X is not readonly
Y.GetHashCode(); // OK — Y is readonly
}
}
Remediation
Make every field combined into GetHashCode either readonly or const, or compute the
hash from properties whose backing state cannot change after construction. If a field
must remain mutable, exclude it from the hash entirely and from Equals as well — the
two methods must agree on what defines the object’s identity.