Exception type does not implement the standard exception constructors

ID

vbnet.maintainability.implement_standard_exception_ctors

Severity

low

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Error Handling

Language

VB.NET

Description

Reports a class that inherits from a type whose name ends in Exception and does not declare the full set of standard exception constructors: the parameterless Sub New(), Sub New(message As String), and Sub New(message As String, innerException As Exception). Missing any one of the three is enough to report the type; it is accepted only once all three are present, in any order, with or without ByVal, and with the inner-exception parameter typed as either Exception or System.Exception. Classes that derive from something other than an exception are not examined.

Rationale

The set of constructors an exception offers decides what a throw site is able to say. With only New() available there is nowhere to put the detail that explains the failure, so the message degrades into whatever the type name conveys and the specifics - the invoice number, the file that was missing, the response the service returned - are either dropped or logged separately from the exception that reports them. With no (message, innerException) overload the author who has to wrap a low-level failure is left choosing between discarding the original exception and letting a persistence or transport exception escape through an abstraction that was meant to hide it; in the first case the InnerException chain that would have named the real fault does not exist, and no amount of downstream logging can rebuild it. The parameterless constructor matters for a different reason: serializers, object mappers and test factories create instances reflectively, and its absence makes the type awkward to handle generically. Adding the constructors later is source-compatible, but every existing throw site has to be revisited to take advantage of them, so the cost grows with the age of the type.

The following code illustrates the pattern detected by this rule:

Namespace Acme.Billing.Errors

    ' FLAGGED: Exception type does not implement the standard exception constructors
    Public Class InvoiceNotFoundException
        Inherits Exception

        Public Property InvoiceId As String
    End Class

Remediation

Declare all three constructors, each delegating to the matching MyBase.New overload so the base type keeps ownership of Message and InnerException. Keep any extra state the exception carries on properties set from the constructors, rather than replacing one of the standard forms with a custom signature.

' Before: callers cannot describe the failure or preserve its cause
Public Class InvoiceNotFoundException
    Inherits Exception

    Public Property InvoiceId As String
End Class

' After
Public Class InvoiceNotFoundException
    Inherits Exception

    Public Property InvoiceId As String

    Public Sub New()
        MyBase.New()
    End Sub

    Public Sub New(message As String)
        MyBase.New(message)
    End Sub

    Public Sub New(message As String, innerException As Exception)
        MyBase.New(message, innerException)
    End Sub
End Class

Configuration

This detector does not need any configuration.