Equals implementation throws an exception
ID |
vbnet.correctness.exception_in_equals |
Severity |
low |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Error Handling |
Language |
VB.NET |
Description
Reports a Throw statement, at any nesting depth, inside a Function Equals(…) As Boolean member - both the Overrides Equals(obj As Object) form and the strongly typed IEquatable(Of T).Equals overload. An Equals implementation that returns False for every argument it cannot consider equal, including Nothing and values of an unrelated type, is not reported.
Rationale
Equals answers a yes/no question and the framework calls it from places that cannot handle a failure. Dictionary and hash-set lookups, List.Contains, LINQ Distinct and GroupBy, and equality comparisons inside sorting all invoke Equals on pairs of items the caller never chose, so an argument of an unexpected type or with a mismatched unit is routine rather than exceptional. Throwing turns an ordinary "these two are different" answer into an exception that surfaces deep inside a collection operation, far from any code that could interpret it, and it breaks the contract every consumer of the type relies on: the same lookup succeeds or fails depending on which items happen to share a hash bucket.
The following code illustrates the pattern detected by this rule:
Public Overrides Function Equals(ByVal obj As Object) As Boolean
Dim other = TryCast(obj, Money)
If other Is Nothing Then
' FLAGGED: Equals implementation throws an exception
Throw New ArgumentException("obj is not a Money value", NameOf(obj))
End If
Return Equals(other)
End Function
Remediation
Return False instead of throwing. Convert the argument with TryCast and return False when the result is Nothing, which covers both a Nothing argument and one of an unrelated type, then compare the fields. When a mismatch of unit, currency or scale currently raises an exception, treat it as a difference and return False. If callers genuinely need a strict comparison that rejects incompatible values, expose it as a separate method and leave Equals total.