No Visible Mutable Static Field

ID

csharp.no_visible_mutable_static_field

Severity

high

Remediation Complexity

medium

Remediation Risk

low

Remediation Effort

medium

Resource

Code Smell

Language

CSharp

Tags

api-design, code_smell, concurrency

Description

Reports static fields that are externally visible — public, protected or protected internal — and can still be reassigned because they are neither const nor readonly. private protected fields are not reported: only derived types inside the declaring assembly can reach them.

The visibility of the containing type is deliberately not considered: shared mutable static state is a defect on its own terms, and an internal type merely narrows the set of writers to one assembly.

Rationale

A visible mutable static field is global state with the access control removed. Any code in any referencing assembly can overwrite it, and there is no way for the declaring type to validate the new value, to log the change, or to keep a related field consistent with it. On top of that the field is unsynchronised: concurrent writes from two threads race, and a reader may observe a value that no writer ever intended. Wrapping the state behind a property restores the seam where validation and locking belong, and static readonly states the more common intent — a value initialised once and read from then on.

public class Registry
{
    public static int Counter;                              // FLAW - anyone can overwrite it
    protected static string Prefix = "cache:";              // FLAW - visible to derived types elsewhere
    public static volatile bool Ready;                      // FLAW - volatile does not make it safe to share

    public const int MaxRetries = 3;                        // OK - immutable
    public static readonly int Limit = 5;                   // OK - assigned once
    private static int hidden;                              // OK - not visible outside the type
    internal static int shared;                             // OK - assembly-scoped
    public int instanceCounter;                             // OK - not static
}

Remediation

Make the field static readonly when it is initialised once, or const when the value is a compile-time literal that will never change. When the value genuinely has to change at run time, make the field private and expose a property or method that owns the invariants and the synchronisation; better still, hold the state in an instance whose lifetime the caller controls, so tests can substitute it.