Skip to content

BRO1105: Constructor initializers should be on their own line

: base(...) and : this(...) go on the line after the constructor's parameters, one level deeper. Replaces StyleCop SA1128.

Example

Before

public class Entity
{
    public Entity(string id)
    {
        Id = id;
    }

    public string Id { get; }
}

public class Customer : Entity
{
    public Customer(string id, string name) : base(id)
    {
        Name = name;
    }

    public string Name { get; }
}

After

public class Entity
{
    public Entity(string id)
    {
        Id = id;
    }

    public string Id { get; }
}

public class Customer : Entity
{
    public Customer(string id, string name)
        : base(id)
    {
        Name = name;
    }

    public string Name { get; }
}

Why

Constructor signatures tend to get long. With the initializer on its own line, the parameters and the call to the other constructor are each easy to read, and changing one doesn't touch the other's line.

How the fix works

The indentation comes from .editorconfig (indent_style, indent_size, tab_width) and defaults to four spaces. Everything after base/this stays as it is: the arguments, even when they span several lines, and a body on the same line (: base() { }). A comment before the colon stays on the constructor's line. A colon at the end of the constructor's line (Customer(...) :) moves down too.

Not reported

  • An initializer that already starts its line.
  • A primary constructor's base list (class D(int x) : B(x)), which isn't a constructor initializer.
  • A comment between the colon and base/this (: /* note */ base()). The fix rewrites exactly that stretch, so it would drop the comment.

Configuration

[*.cs]
stylebro_constructor_initializer_placement = own_line
  • own_line (default): like StyleCop's SA1128, as described above.
  • same_line: the initializer goes on the line of the parameter list's ):
// Before
public Customer(string id, string name)
    : base(id)
{
}

// After
public Customer(string id, string name) : base(id)
{
}

With same_line, an initializer is only joined (and only reported) when:

  • the line of the ) with : base(...) added, up to the initializer's end, fits max_line_length. A body after the initializer ({ }, => ...) isn't counted: BRO1509 may move it. Without max_line_length there is no limit, so set it when you use same_line.
  • the initializer is on one line (base( with arguments on further lines is left alone),
  • nothing but whitespace sits between ) and base/this (no comments or directives).

When the parameter list is split, the initializer goes after the ) on its last line. A ) on its own line that BRO1110 moves to the last parameter moves in the same edit (int b) : base(b)), so both fixes give the same text whichever runs first. The ) line is measured as the other fixes in the same run leave it, when those rules are on: BRO1404/BRO1007 add an access modifier, BRO1109 moves ( up, BRO1108 puts each parameter on its own line, BRO1110 moves ) (its own_line setting puts it on a line of its own, where the initializer then joins it). So the decision doesn't depend on which fix runs first. Not counted: renames (BRO1302, BRO1309, ...) and rewrites inside the line (BRO1115 int?, BRO1144 @, ...), whose results depend on checks made elsewhere (a rename can be skipped). Right at the limit, one of those can still leave a join for a second dotnet format run, or a joined line a few characters too long.

Compared with StyleCop

Same results as StyleCop. StyleCop has no same_line setting; teams that prefer it turn SA1128 off.