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.

Configuration

This detector does not need any configuration.