Exception In Equals
ID |
csharp.exception_in_equals |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
CSharp |
Tags |
equals, reliability |
Description
Reports overrides of Equals(object) that contain a throw statement. Equals must never
throw: the runtime contract requires it to accept any argument, including null, and return
either true or false.
Rationale
Equals is invoked implicitly by many framework APIs: hash tables and dictionaries during
lookup, LINQ operators such as Contains and Distinct, the == operator on user-defined
types, equality comparers, and serialisation. Throwing from Equals surfaces failures in
unrelated, hard-to-diagnose call sites, breaks the runtime contract, and can corrupt
collection invariants.
The supported response for an argument that cannot be compared is to return false, not to
throw.
public class Money
{
private readonly decimal amount;
public override bool Equals(object obj)
{
if (obj is not Money)
throw new ArgumentException("not a Money"); // FLAW — Equals must not throw
return amount == ((Money)obj).amount;
}
}
public class MoneyOk
{
private readonly decimal amount;
public override bool Equals(object obj) // OK — returns false for foreign types
{
return obj is MoneyOk other && amount == other.amount;
}
public override int GetHashCode() => amount.GetHashCode();
}