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