Property is declared WriteOnly

ID

vbnet.maintainability.properties_should_not_be_write_only

Severity

low

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Api Design

Language

VB.NET

Description

Reports a property declared with the WriteOnly modifier, which gives it a Set accessor and no Get. Both ways of writing the declaration are covered, with and without the empty parentheses after the property name - WriteOnly Property Password As String and WriteOnly Property Password() As String. Properties that expose both accessors, properties declared ReadOnly and fields are not reported, and neither is a Sub that takes the value as a parameter, which is the shape this rule asks for instead.

Rationale

A property is the syntax for a value the object has; writing to one and being unable to read it back contradicts that at the call site. credentials.Password = value looks like state being stored, so the next author reasonably writes If credentials.Password Is Nothing and only finds out at compile time that the value went somewhere unreadable. The consequences are practical, not stylistic: object initialisers, data binding, serializers and object mappers all pair a setter with a getter, so a WriteOnly property round-trips as a hole - the value is applied on the way in and silently dropped on the way out, which turns into a configuration that saves and reloads as empty. Where the reason for having no getter is that the value must not be readable - a password, a token, a connection string - the property form advertises the opposite of that intent; a method named for what it does states it. And where the setter validates or transforms the value, its name cannot say so, while a Sub can.

The following code illustrates the pattern detected by this rule:

Private _destinationId As Integer

' FLAGGED: Property is declared WriteOnly
Public WriteOnly Property Password As String
    Set(value As String)
        _password = Protect(value)
    End Set
End Property

' FLAGGED: Property is declared WriteOnly
Protected Overridable WriteOnly Property ConnectionString As String
    Set(value As String)
        _connectionString = value
    End Set
End Property

' The VB6-era declaration form, with empty parentheses after the name. This is what
' long-lived VB.NET code is actually written in - all 85 WriteOnly properties in the
' reference corpus use it. (shape from cms App_Code/PDF.vb:636)
' FLAGGED: Property is declared WriteOnly
Public WriteOnly Property EncodeASCII85() As Boolean
    Set(ByVal vData As Boolean)
        _encodeAscii85 = vData
    End Set
End Property

' ...and the same form with a modifier in front. (shape from kartris KartrisClasses.vb:293)

Remediation

Replace the property with a Sub that takes the value as a parameter, named for the operation it performs - SetPassword(value), Configure(connectionString). If the value genuinely is readable state, add the Get accessor and drop WriteOnly; when only the fact that a value was supplied should be observable, expose a separate ReadOnly property such as HasPassword rather than the value itself.

' Before: assignable but not readable, and dropped by serializers and data binding
Public WriteOnly Property Password As String
    Set(value As String)
        _password = Protect(value)
    End Set
End Property

' After
Public Sub SetPassword(value As String)
    _password = Protect(value)
End Sub

Public ReadOnly Property HasPassword As Boolean
    Get
        Return Not String.IsNullOrEmpty(_password)
    End Get
End Property

Configuration

This detector does not need any configuration.