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.