Method declares an Optional parameter with a default value
ID |
vbnet.maintainability.no_optional_params |
Severity |
high |
Remediation Complexity |
medium |
Remediation Risk |
low |
Remediation Effort |
medium |
Resource |
Api Design |
Language |
VB.NET |
Description
Reports a Sub or Function that is not declared Private and that declares an Optional
parameter with a default value. Private methods are not reported, since the argument is about
the published surface rather than about the construct itself.
Rationale
A default value is not part of the called method at run time - the compiler copies it into every
call site that omits the argument. Changing Optional retries As Integer = 3 to = 5 therefore
changes nothing for any assembly already compiled against the old default: those callers keep
passing 3 until they are rebuilt, so the same call in two projects behaves differently and the
source gives no hint of it. That makes a default value a versioning commitment disguised as a
convenience. Optional parameters also weaken the signature as documentation of what the operation
needs, and the weakness compounds: with several optional parameters the reader of a call site has
to know the declaration order to understand Send(msg, , "audit"), and adding a parameter in the
middle silently rebinds arguments in callers that pass positionally. Explicit overloads avoid all
of this - each overload is a real method that can be versioned, documented and found by tooling -
and the one that takes fewer arguments can supply the default by calling the fuller one, so the
value lives in exactly one place.
The following code illustrates the pattern detected by this rule:
Public Class Notifier
' FLAGGED: Method declares an Optional parameter with a default value
Public Sub Send(message As String, Optional retries As Integer = 3)
Dispatch(message, retries)
End Sub
Remediation
Replace the optional parameter with overloads: keep one method that takes the full argument list, and add a shorter overload that supplies the default and delegates to it, so the default value exists once in your own code rather than in every caller. Where the number of options makes that impractical, take an options object or use named arguments against a single full signature.
' Before: the default is copied into each call site and cannot be changed for compiled callers
Public Sub Send(message As String, Optional retries As Integer = 3)
' After
Public Sub Send(message As String)
Send(message, 3)
End Sub
Public Sub Send(message As String, retries As Integer)