Base Equals Reference

ID

csharp.base_equals_reference

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

CSharp

Tags

equals, reliability

Description

Reports Equals(object obj) overrides whose body simply returns base.Equals(obj). On a non-record class this delegates to System.Object.Equals, which is reference identity, so the override adds nothing — and any caller that expected structural equality gets broken behaviour.

Rationale

If you override Equals you almost always want structural equality (compare fields). base.Equals(obj) on object is reference identity, which means the override returns true only when both arguments are the very same instance. Records are exempt because the compiler-generated structural Equals lives on the record type itself, so delegating to it from a record’s explicit override is legitimate.

public class Money
{
    public decimal Amount { get; }

    public override bool Equals(object obj)
    {
        return base.Equals(obj);                        // FLAW — reference identity
    }
}

public class MoneyOk
{
    public decimal Amount { get; }

    public override bool Equals(object obj)
    {
        return obj is MoneyOk m && m.Amount == Amount;  // OK
    }

    public override int GetHashCode() => Amount.GetHashCode();
}

Remediation

Compare the relevant state fields directly. If you actually want reference identity, remove the override — that is what the inherited Equals already does. For value-like types, prefer C# records, which synthesise consistent Equals/GetHashCode.