Override Equals On Overloading
ID |
csharp.override_equals_on_overloading |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
CSharp |
Tags |
equals, reliability |
Description
Reports class or struct types that overload operator == or operator != but do not
also override Equals(object obj). Without the matching Equals, the == operator and
the equality-by-method path disagree for the same operands.
Rationale
== is a static call, dispatched at compile time by operand type, while Equals is a
virtual call through System.Object. Collections (Dictionary, HashSet, LINQ) and
many framework APIs use Equals. If the two are inconsistent, the same pair of objects
will compare equal under one path and unequal under the other — a notoriously hard bug to
diagnose.
public class Money // FLAW
{
public decimal Amount { get; }
public static bool operator ==(Money a, Money b) =>
a is not null && b is not null && a.Amount == b.Amount;
public static bool operator !=(Money a, Money b) => !(a == b);
}
public class MoneyOk
{
public decimal Amount { get; }
public static bool operator ==(MoneyOk a, MoneyOk b) => // OK
a is not null && b is not null && a.Amount == b.Amount;
public static bool operator !=(MoneyOk a, MoneyOk b) => !(a == b);
public override bool Equals(object obj) =>
obj is MoneyOk m && m.Amount == Amount;
public override int GetHashCode() => Amount.GetHashCode();
}