Constructorargument Param Exists

ID

csharp.constructorargument_param_exists

Severity

high

Remediation Complexity

medium

Remediation Risk

low

Remediation Effort

medium

Resource

Code Smell

Language

CSharp

Tags

api-design, code_smell, markup-extension, string-wiring

Description

Reports a property carrying [ConstructorArgument("name")] where name matches no parameter of any constructor of the declaring class. The attribute pairs a property with a constructor parameter so that markup-extension tooling can round-trip the extension back to its positional form; the pairing is made by string, so nothing checks it at compile time.

Rationale

[ConstructorArgument] tells a designer or serializer that this property holds the value that was passed as that constructor argument. With a correct name, the extension can be written back as {Lookup someKey}; with a wrong one, the property is simply not recognised as corresponding to any argument and the value does not make it back out.

Nothing throws. The build is clean, the extension still works when constructed from markup, and only the round trip is broken — so a typo, a renamed parameter or a capitalization slip can survive a long time before anyone notices a serialized form that has lost a value. Matching is case-sensitive, which is where most of these come from.

public class LookupExtension : MarkupExtension
{
    public LookupExtension(string key)
    {
        Key = key;
    }

    [ConstructorArgument("keyName")]        // FLAW — the parameter is named key
    public string Key { get; set; }

    public override object ProvideValue(IServiceProvider serviceProvider) => Key;
}

public class RangeExtension : MarkupExtension
{
    public RangeExtension(int low, int high)
    {
        Low = low;
        High = high;
    }

    [ConstructorArgument("low")]            // OK — matches a parameter exactly
    public int Low { get; set; }

    [ConstructorArgument("High")]           // FLAW — the parameter is named high, lowercase
    public int High { get; set; }

    public override object ProvideValue(IServiceProvider serviceProvider) => Low;
}

Parameters of every constructor are considered, so a property matching an overload’s parameter is not reported. A class that declares no instance constructor at all is left alone: there is nothing for the name to match, and the missing constructor is the real problem. So is an argument that is not a string literal, whose value the rule cannot read.

Remediation

Correct the string to the exact name of the constructor parameter the property carries, including its casing. If the parameter was renamed, rename it back or update every attribute that refers to it. If the property does not correspond to a constructor argument at all, remove the attribute.