Wrong Default Value Attribute
ID |
csharp.wrong_default_value_attribute |
Severity |
high |
Remediation Complexity |
medium |
Remediation Risk |
low |
Remediation Effort |
medium |
Resource |
Code Smell |
Language |
CSharp |
Tags |
attributes, code_smell, default-value, parameters |
Description
Reports a parameter carrying [Optional] together with [DefaultValue] and without
[DefaultParameterValue]. The parameter is then omissible but has no default: [DefaultValue] is
design-time metadata and supplies no value to a caller. Every attribute is recognised in both its
plain and its Attribute-suffixed spelling, qualified or not.
Not reported: [DefaultValue] on a property or a field, which is its intended use; [DefaultValue]
on a parameter that is not [Optional], which is metadata rather than a broken default; and a
parameter that also carries [DefaultParameterValue], where the value is supplied correctly.
Rationale
Two attributes with almost the same name do very different things. DefaultValue tells designers,
property browsers, code generators and API schema tools which value counts as "unchanged", so they
can leave it out of what they emit. DefaultParameterValue, together with Optional, is the one
that puts a default in the metadata a caller can be given.
Combining Optional with DefaultValue therefore produces an optional parameter with no default at
all — an omitting caller gets default(T):
using System.ComponentModel;
using System.Runtime.InteropServices;
public class Settings
{
[DefaultValue(true)]
public bool Verbose { get; set; } // OK - designed use
public void Configure([Optional, DefaultValue(4)] int retries) { } // FLAW - an omitting caller gets 0
public void Tune([Optional, DefaultParameterValue(4)] int retries) { } // OK
public void Describe([DefaultValue(4)] int retries) { } // OK - metadata, not an interop default
public void Scale(int factor = 2) { } // OK - the plain C# way
}
Nothing warns about it. The declaration reads as if the parameter had a default, the file compiles, and the gap surfaces only when an interop or reflection caller omits the argument and silently receives a zero, a null or an empty string where the author believed a value was in play.
Remediation
In ordinary C# code, use the language’s own syntax: int retries = 4. It is checked by the compiler,
it appears in the signature where readers look for it, and callers can simply omit the argument. Keep
[Optional] with [DefaultParameterValue] for the cases that need the attribute form — COM interop
and signatures consumed by other languages. Leave [DefaultValue] for the properties and fields that
designers read.