Last Updated: May 17, 2026
Declaring a class and creating objects from it with new does more than allocate memory: it runs a special method called a constructor, whose job is to put the object into a valid initial state. This chapter is about how constructors work, how to define your own, how to overload them, how to chain them within the same class, and how a separate animal called a static constructor fits into the picture. By the end you should be able to look at any class and predict exactly which code runs, and in what order, when someone writes new YourClass(...).
A class without a constructor is fine, but a class with no way to initialize its fields is mostly useless. Take a Product with three fields:
Every field gets its type's default value: null for string (printed as an empty string), 0 for decimal, 0 for int. The object exists, but it doesn't represent a real product. Someone now has to set each field by hand before this object is good for anything, and there's no compiler check to make sure they do.
A constructor solves this. It's a method that runs automatically when you write new Product(...), and you can use it to require the caller to supply the values needed to make a valid object.
Now new Product() won't compile. The only way to create a Product is through the constructor, and the constructor demands the three values it needs. The object can't exist in the broken half-built state from the first example.
That's the whole point: a constructor is the gate between "raw memory" and "a usable object." Everything else in this chapter is variation on the same theme.
A constructor looks like a method with three rules:
void.new ClassName(...).new site.The new Customer("Alice", "alice@shop.com") call does three things in order: allocates memory for a Customer object on the heap, runs the constructor body with the arguments bound to the parameters, and returns a reference to the new object. The variable c holds that reference.
A constructor can have any access modifier (public, internal, private, protected), but most are public because that's what lets outside code create instances. We'll see one use of a private constructor near the end.
If you don't write any constructor at all, C# gives you one for free. It's called the default constructor: it takes no parameters and does nothing except initialize each field to its type's default value (0, null, false, etc.). This is what made new Product() work in the very first example.
The catch: the moment you declare any constructor of your own, the free default constructor goes away. The compiler stops generating it.
If you still want a parameterless constructor after adding a parameterized one, you have to declare it yourself:
The explicit parameterless constructor sets Owner to "Guest". It's not the same as the free default constructor (which would have left Owner as null), because you're free to put any code you like in the body.
Cost: Declaring any constructor removes the free default. Code that used to compile with new ClassName() will stop compiling. If you add a parameterized constructor to a public type that previously had none, you're breaking the binary contract for anyone calling new ClassName().
A constructor with parameters is the most common shape. It forces the caller to supply enough information to build a usable object, and it gives the class a place to validate that information before the object exists.
The validation runs before the assignment. If any check fails, the constructor throws, and new Product(...) never produces an object: the bad variable is never assigned. This is the central guarantee constructors give you: by the time you hold a reference to a Product, that Product has already passed every check the constructor enforces.
Compare that to setting fields after construction. There's no way to require it, and no way to validate, because the object already exists. A class designed around its constructor pushes invalid state out of the universe entirely. That's a strong design lever and one of the main reasons constructors exist as a separate concept from regular methods.
A class can declare more than one constructor, as long as the parameter lists differ in number or type. The compiler picks the matching constructor based on the arguments at the new site. This is just method overloading applied to constructors.
Three constructors, each with a different "shape" of caller input. The compiler resolves new Order(2, "Bob", 49.99m) to the three-argument constructor and new Order(1, "Alice") to the two-argument one.
This works, but look closely. Each constructor repeats the same field assignments. If you ever change how Status is initialized, you have to remember to update three places. That's the kind of duplication that quietly rots a codebase. Constructor chaining, coming up next, fixes it.
Optional parameters with default values cover a lot of the same ground:
One constructor, four call shapes. So when do you reach for overloading instead of optional parameters? A few cases:
new Money(100) and new Money("USD 100") can't share one signature.For pure "more parameters means more configurability," optional parameters are usually the lighter choice.
For a constructor whose body is a single statement, C# 7 added expression-bodied syntax with =>. It's the same shorthand you've seen for methods.
becomes
Both versions compile to the same IL. The expression-bodied form is just less noise for one-line bodies.
Price and Stock got their type defaults because the constructor only set Name.
The expression-bodied form has a hard limit: one statement. The second you need two field assignments or a validation check, you go back to the block form. Don't try to cram multiple statements into the expression body with the comma operator or sequence tricks; the syntax doesn't support it, and even if it did, the result would be unreadable.
: this(...)The duplicated assignments in the Order overload example were a symptom: those three constructors were really one constructor with two shortcut versions. C# lets you express that directly. A constructor can call another constructor of the same class using : this(...) after its parameter list. The chained constructor runs first, then your body runs.
Same result as before, but the field-assignment logic lives in exactly one place. The two shorter constructors have empty bodies ({ }) because all their work is "delegated" to the four-argument constructor through : this(...). If you later need to validate customer, you add the check once, in the four-argument constructor, and every overload picks it up.
A quick diagram of how a new Order(1, "Alice") call flows:
The two-argument constructor never assigns fields directly. It hands control to the four-argument constructor through : this(...), which does the work and returns. Then the two-argument constructor's body would run (if it had one), and finally the new object is handed back to the caller.
A useful pattern: pick one constructor as the "primary" one that does all the real work, and have every other constructor chain to it. New developers reading the class can find the validation and assignment logic in one place. This pattern is sometimes called the delegating constructor or funnel pattern.
Before we get to static constructors, one more place where initialization code can live: directly on the field declaration. You can give a field a starting value right where it's declared.
The = "Guest", = 0m, and = new List<string>() are field initializers. They run for every new instance, before the constructor body. So if a class has both field initializers and a constructor, the field initializers go first, then the constructor body.
If the constructor assigns the same field, its assignment overwrites the initializer's value. Don't use field initializers and constructor assignments to set the same field, since one of them is dead code by definition. Pick one place per field.
Field initializers are a clean way to set a default value that's the same for every instance, like Items = new List<string>() or Status = "Placed". They can't reference constructor parameters (the parameter doesn't exist yet when the initializer runs) or call any non-static instance member of the class being constructed.
Everything so far has been about initializing one instance at a time. A static constructor is different. It runs once per type, not once per object, and it's where you put initialization code for static fields.
A static field belongs to the class itself, not to any instance. There's one copy of it shared across the whole program, no matter how many instances you create. Examples are things like a counter for "how many Order objects have ever been created" or a shared lookup table.
TotalOrdersCreated got its default value of 0 automatically. But what if you want a more involved setup for a static field, say loading a list of valid order statuses from somewhere? That's what a static constructor is for.
The static constructor (static Order()) ran exactly once, right before the first time the runtime needed the class. That happened to be the first new Order(...) call. After that, no matter how many Order objects we create, the static constructor never runs again.
Static constructors have a tight set of rules, all of which exist because they run automatically rather than at your request:
public static Order() or private static Order(). The runtime is the only caller, so visibility is meaningless. Just static Order().AppDomain. After it runs, it never runs again.Cost: Static constructors run automatically when the type is first used, and any exception they throw becomes a TypeInitializationException that wraps the original. Worse, once a type fails to initialize, every future use of it in that process throws the same exception. Don't put I/O, network calls, or anything that can fail unpredictably in a static constructor.
The honest answer is "less often than you might think." Most static fields can use a field initializer:
That's a single-line initializer doing the same job as a static constructor. You only need an actual static constructor when:
For everything else, prefer field initializers. They're more direct and harder to misuse.
Pulling all the pieces together, here's the exact sequence that runs when you create an instance of a class for the first time. Knowing this order is the difference between confidently predicting behavior and guessing.
For the first ever use of a type in a program:
For every subsequent instance, steps 1 and 2 are already done and don't run again. Only steps 3 and 4 run.
The static side runs exactly once, at the boundary between "we've never touched this type" and "we're using it now." The instance side runs every time a new object is built.
Here's the same sequence as a flow diagram, which is sometimes easier to keep in your head:
The "Yes" branch is the common case after the first use: skip the static work, run the instance work, return. The "No" branch happens exactly once per type per process.
This order is also why static initializers can't refer to instance members and why instance initializers can refer to static members. By the time an instance initializer runs, all the static state is already in place. The reverse isn't true.
Stepping back from the rules, here's a ShoppingCart class that uses several of the features from this chapter together:
A few things worth noticing:
Items and Total are set through field initializers because every cart starts the same way. There's no reason to repeat Items = new List<string>() in three constructors.CartsCreated is incremented inside the two-argument constructor. Because the other two chain to it, every cart creation counts exactly once. If we'd increment in each constructor instead, calls through the chain would double-count.CartsCreated = 0 is a simple enough initializer to live on the field.This is the shape most real C# classes end up in: one well-validated "main" constructor, one or more chained shortcuts, field initializers for "every instance starts this way" state, and a static field or two if the class needs to track something across all instances.
new call and initializes the object before the reference is handed back.public T(...) => Field = value;) are syntactic sugar for one-statement constructors.: this(...) chains one constructor to another in the same class. Use it to funnel every overload through a single "primary" constructor that holds the validation and assignment logic.static ClassName()) runs exactly once per type per process, automatically, before the type is first used. It has no access modifier and no parameters.The _this Keyword_ lesson covers the this keyword in more depth. We've already seen it in : this(...) constructor chaining. Inside any instance method or constructor, this is also a reference to the current object itself, and it has a handful of uses, from disambiguating field and parameter names to passing the current instance to another method.