Summary#
A constructor initializes a class instance. It is declared as a member whose name is the class name and
which has no return type; you invoke it with new T(args). The constructor body runs first, then any
object initializers { Member = value } are applied on top (so an initializer wins over a value the body
set). new T(args) evaluates to the constructed object.
Signature#
class T {
public T(<Type> p1, <Type> p2 = <default>) { // no return type
<statements> // may read the params and assign this fields
}
public T() : this(<a>, <b>) { … } // another one, chaining to the first
}
new T(a, b) // run the constructor these arguments select
new T(a, b) { Note = "x" } // …then apply the initializerDescription#
- A class may declare several constructors, told apart by their parameter TYPES exactly as
methods are —
new T(args)picks the one its arguments fit. Two that differ only in parameter NAMES are the same constructor declared twice, and a compile error. The name is the class name and there is no return type (areturn;with a value is a compile error). : this(…)chains to another constructor of the same class, which runs FIRST — so shared setup lives in one place. The chained-to constructor is the one that runs: base(…); a chaining constructor does not also run the base's, so a base constructor runs exactly once. A chain that comes back round to where it started is a compile error rather than unbounded recursion at construction time.- Visibility follows the class-member rule:
publicmakes it callable from anywhere; a bare (unmarked) constructor is private — callable only from within its own class (the factory-method pattern). new T(args)binds the arguments to the constructor's parameters (positional orname: value, with C# default-parameter values for omitted optionals), runs the body with the fresh instance in scope, and produces that instance.- Initializers run after the body:
new T(args) { Member = value }runs the constructor first, then assigns each initializer — so an initializer overrides whatever the constructor set for that member (C# order). Initializer values cannot reference the object being constructed. - A class with no declared constructor uses the implicit default:
new T()andnew T { … }build the instance with no user code. Passing arguments to such a class is a compile error. - A field initializer runs before every constructor body (
decimal Rate = 0.25m;), so the body reads the declared value and may overwrite it — C#'s order. Object initializers are the ones that run after. - A required parameter must be supplied: if the constructor has a non-optional parameter,
new T { … }(no arguments) is a compile error — pass the argument. - Durable: a suspension inside a constructor body survives serialize/restore, and
new T(args)still evaluates to the same constructed instance on resume.
Examples#
class Order {
public int Qty;
public decimal Total;
public string Note;
// The ctor assigns every required member, so `new Order(5, 2m)` compiles without also naming them in an
// initializer — the compiler infers that the constructor satisfies them (no annotation needed).
public Order(int qty, decimal price) {
Qty = qty;
Total = qty * price;
Note = "new order";
}
}
// The constructor sets the fields from its arguments.
decimal OrderTotal() {
var o = new Order(5, 2m);
return o.Total; // 10 (5 * 2, set in the body)
}
// An initializer is applied AFTER the body — so it wins.
string OrderNote() {
var o = new Order(5, 2m) { Note = "rush" };
return o.Note; // "rush" (the body set "new order"; the initializer overrode it)
}class Box {
public int Size;
public Box(int size = 3) { Size = size; }
}
int DefaultSize() { var b = new Box(); return b.Size; } // 3 (the constructor's default)
int GivenSize() { var b = new Box(7); return b.Size; } // 7
// A class with no declared constructor keeps the plain build.
class Point { public int X; public int Y; }
int Origin() { var p = new Point { X = 0, Y = 0 }; return p.X + p.Y; } // 0Several constructors, and chaining between them#
Declare as many as differ in their parameter types. : this(…) runs another of them first, which is how shared
setup stays in one place:
class Panel {
public decimal Width;
public decimal Height;
public Panel(decimal width, decimal height) { // the one that actually assigns
Width = width;
Height = height;
}
public Panel(decimal width) : this(width, 20m) { } // chains to it
public Panel() : this(10m) { } // …and so does this, one hop further
}Each new picks by its arguments, and a chain runs all the way down before the chaining body:
[Test]
void Constructors_Select_And_Chain() {
Assert.Equal(3m, new Panel(3m, 4m).Width);
Assert.Equal(20m, new Panel(3m).Height); // the default the one-arg constructor passed on
Assert.Equal(10m, new Panel().Width); // two hops: () → (decimal) → (decimal, decimal)
}See also#
- class methods — instance methods on a class (the same member-body mechanism the constructor reuses)
- Typed locals — declaring the local that holds a constructed instance