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.