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)

Configuration

This detector does not need any configuration.