No Public Const Member
ID |
csharp.no_public_const_member |
Severity |
high |
Remediation Complexity |
medium |
Remediation Risk |
low |
Remediation Effort |
medium |
Resource |
Code Smell |
Language |
CSharp |
Tags |
api-design, binary-compatibility, code_smell |
Description
Reports const members that are externally visible — public, protected or
protected internal. private protected constants are not reported, nor are const locals
inside a method body, nor constants declared in a type that is not itself externally visible:
without a cross-assembly consumer there is nothing to break.
Rationale
A const is a compile-time value. The compiler copies the literal into every place that
reads it, including places in other assemblies, so the value becomes part of those assemblies
the moment they are built. Publishing a corrected version of the declaring assembly changes
nothing for them: they keep using the old number until each one is rebuilt, and a mixed
deployment then runs two different values for the same named constant. static readonly is
read from the declaring assembly at run time, so updating that one assembly updates every
consumer.
Keep const for values that are part of the type’s identity and can never change — a
mathematical constant, a protocol magic number fixed by a specification — and for
private/internal values whose only consumers are recompiled together with the
declaration.
public class Limits
{
public const int MaxRetries = 3; // FLAW - inlined into every consumer
protected const string Prefix = "cache:"; // FLAW - same, for derived types elsewhere
internal const int BatchSize = 50; // OK - no cross-assembly consumer
private const int Slack = 2; // OK
public static readonly int Timeout = 30; // OK - read at run time
public int Compute()
{
const int localFactor = 7; // OK - a local const has no consumers
return localFactor * MaxRetries;
}
}
Remediation
Change the member to public static readonly and keep the same initialiser. Two cases need a
second look: an attribute argument, a switch case label or a default parameter value
requires a compile-time constant, so those call sites will not compile against a
static readonly field and need a different design (for example a static property plus an
overload). If the value truly cannot change, keep the const and treat it as part of the
published contract.