Unassigned Readonly Field

ID

csharp.unassigned_readonly_field

Severity

low

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

CSharp

Tags

field, reliability

Description

Reports readonly fields that have neither an inline initializer nor an assignment in any constructor of the enclosing type. Such fields are silently left at the type’s default value (zero, false, or null).

Rationale

readonly advertises that the value is set once and never mutated again. If no initialisation runs, every consumer reads the default value, which usually leads to `NullReferenceException`s or subtle wrong-default bugs. The compiler will not complain because a missing assignment is a valid program, but it is almost never the developer’s intent.

public class Account
{
    private readonly string _id;                     // FLAW — never assigned
    private readonly DateTime _created;              // FLAW

    public Account()
    {
        // forgot to assign _id and _created
    }
}

public class AccountOk
{
    private readonly string _id = Guid.NewGuid().ToString();  // OK
    private readonly DateTime _created;

    public AccountOk(DateTime created)
    {
        _created = created;                          // OK
    }
}

public class ExpressionBodiedOk
{
    private readonly int _value;                      // OK — assigned by the ctor's whole body

    public ExpressionBodiedOk(int value) => _value = value;
}

public class TupleDeconstructedOk
{
    private readonly int _a;                          // OK
    private readonly int _b;                          // OK

    public TupleDeconstructedOk()
    {
        (_a, _b) = ComputePair();                     // both assigned via deconstruction
    }

    private static (int, int) ComputePair() => (1, 2);
}

Two assignment shapes are recognised alongside the plain field = value; and this.field = value; forms: an expression-bodied constructor whose entire body is the assignment (⇒ field = value;), and a tuple-deconstruction assignment where the field is one of the targets ((field1, field2) = expr;).

Remediation

Assign the field either inline at the declaration or unconditionally in every constructor. If the value cannot be known until after construction, the field probably should not be readonly.