C# Constructor Chaining with 'this'
c# this constructor: Learn how to use the 'this' keyword to chain constructors in C#, reduce duplication, and keep initialization logic consistent.
When you declare multiple constructors in a C# class, you often find the same initialization logic repeated in each one. The this keyword lets one constructor call another constructor in the same class, so you can centralize that logic and keep each overload focused. This pattern, called constructor chaining, is common in real-world C# code, but it has rules and subtle behavior that matter more than the syntax itself.
The c# this constructor syntax appears as a constructor initializer, placed after the parameter list and before the constructor body. For example:
public class Order { public Order() : this("default") { } public Order(string number) { Number = number; } }
Here the parameterless constructor delegates to the Order(string) constructor, passing a literal default value. When a caller creates new Order(), the chained constructor runs first, then the calling constructor's body executes. The opposite is not allowed: you cannot call the parameterless constructor from a constructor with parameters using this() because that would require a parameterless constructor to exist unless you provide arguments.
Why Chain Constructors Instead of Duplicating Code
Suppose you have a class that requires several fields to be initialized, and you want to offer different ways to construct it. Without chaining, you might copy the assignment statements into each constructor.
public class Customer { private string _name; private string _email; private bool _isActive; public Customer(string name) { _name = name; _email = ""; _isActive = true; } public Customer(string name, string email) { _name = name; _email = email; _isActive = true; } }
The duplication is harmless in a small example, but it becomes a maintenance problem as fields grow. If a new field is added, every constructor must be updated, and forgetting one produces inconsistent behavior. Chaining removes that risk by having all constructors funnel through a single implementation.
public class Customer { private string _name; private string _email; private bool _isActive; public Customer(string name) : this(name, "") { } public Customer(string name, string email) { _name = name; _email = email; _isActive = true; } }
Now the first constructor delegates to the second, and all validation or field assignments live in one place. The behavior is identical for callers, but the code is easier to follow and modify.
Rules That Apply to the this Constructor Initializer
The initializer must be the first statement after the parameter list, before the opening brace of the body. The compiler enforces this ordering, so you cannot place any code before it.
public class Example { public Example() : this(10) { } public Example(int value) { } }
Inside the initializer, you can pass any expression that is valid at that point, including constants, static fields, and values derived from the constructor's parameters. You cannot use instance fields because the object does not exist yet. You also cannot use base in the same initializer because you can only choose one constructor to call: either this(...) or base(...). If you do not write either, the compiler implicitly calls the parameterless constructor of the base class, if one exists.
A common mistake is trying to use this() inside a constructor body to call another constructor. That is invalid syntax; the call must be in the initializer position. The compiler reports an error because a constructor body cannot contain a direct call to a constructor.
Chaining with Optional Parameters
You might be tempted to use optional parameters instead of constructor chaining. The choice depends on what you need to communicate.
public class Product { public Product(string name, decimal price = 0m, bool inStock = false) { // ... } }
Callers can omit trailing arguments, but they cannot omit a middle one without naming it. With chaining, you can expose distinct overloads that are clearer at the call site, such as new Product("Laptop") versus new Product("Laptop", 999.99m, true). Optional parameters also allow any caller to supply a value for price while ignoring inStock, which may be a useful flexibility. However, optional parameters are baked into the method signature, so changing a default value is a breaking change for binary consumers. Chaining gives you more control over which combinations are allowed.
Interaction with Base Class Constructors
When a class inherits from another, constructor chaining works independently in each class. A derived constructor can call a constructor in the same class with this, or a constructor in the base class with base. If a derived constructor does not specify either, the compiler looks for a parameterless constructor in the base class. If none exists, the code does not compile.
public class BaseModel { public BaseModel(int id) { } } public class DerivedModel : BaseModel { public DerivedModel() : base(0) { } public DerivedModel(int id) : base(id) { } }
If BaseModel has no parameterless constructor, a DerivedModel constructor must explicitly call base(...). Chaining with this does not change that requirement; it only changes which constructor of the same class is invoked.
A Realistic Example: Validation in a Single Constructor
Consider a class that performs validation during construction. Chaining makes sure every overload goes through the same checks.
public class Invoice { public string Number { get; } public decimal Amount { get; } public Invoice() : this("", 0m) { } public Invoice(string number) : this(number, 0m) { } public Invoice(string number, decimal amount) { if (string.IsNullOrWhiteSpace(number)) { throw new ArgumentException("Number is required.", nameof(number)); } if (amount < 0) { throw new ArgumentOutOfRangeException(nameof(amount)); } Number = number; Amount = amount; } }
All three constructors eventually invoke the three-parameter version. The parameterless constructor is arguably dubious because it passes an empty string that will fail validation, but it might be acceptable if you intend to allow default values that are later set through properties. If validation must never be bypassed, you might remove the parameterless overload. Chaining does not force you to expose every possible combination; you decide which overloads are public.
Common Misunderstandings and Pitfalls
One misunderstanding is that chaining creates a new object instance. The this initializer does not construct a separate object; it runs another constructor on the same object being created. That means any work done in the chained constructor is part of the same instance.
Another pitfall is ordering. The chained constructor executes before the body of the calling constructor. If you have logic that must run before any member initialization, be aware that the chained constructor runs first. Instance field initializers run before any constructor body, including chained ones, so they cannot rely on values set in another constructor.
You can also use this in a constructor initializer to call a constructor that is private or protected, as long as it is accessible from the calling constructor. This is useful when you want to expose only selective overloads while keeping the full implementation private.
When Constructor Chaining Hurts Readability
Excessive chaining can obscure the intent, especially when the chain grows to several levels. If you have a constructor that simply passes a value to a base constructor, adding a one-line chain is fine. But if you have ten overloads that all funnel into a huge constructor, you might be better off with a single constructor and optional parameters, or even a static factory method that supplies defaults. The goal is to reduce duplication without hiding the essential flow.
Chaining also couples every public constructor to the one it calls. If you later change the signature of the final constructor, you must update every chain. That is usually acceptable because the chain is local to the class.
Maintainability and Runtime Cost
The runtime cost of chaining is negligible. The compiled IL calls the target constructor directly, and the object is allocated once. No additional instances are created. The main cost is in code clarity and design. Keeping initialization logic in a single place reduces the chance of missing a field or performing inconsistent validation, which directly improves maintainability in a production codebase.
One production consideration is that chaining does not work with readonly fields if you need to conditionally assign them based on which overload was called. You can assign a readonly field in the chained constructor, but you cannot assign it in the calling constructor after the chain runs. If you must conditionally set a readonly field, you need a different design, such as a static factory method that chooses the constructor.
Combining Chaining with Static Factory Methods
You can use constructor chaining internally while exposing static factory methods to callers. This gives you the flexibility of named construction while keeping the actual object creation straightforward.
public class Configuration { public string ConnectionString { get; } public int TimeoutSeconds { get; } private Configuration(string connectionString, int timeoutSeconds) { ConnectionString = connectionString; TimeoutSeconds = timeoutSeconds; } public static Configuration FromFile(string path) { var data = File.ReadAllLines(path); return new Configuration(data[0], int.Parse(data[1])); } }
Here the constructor is private, so callers must use the factory. The factory can validate the file contents before invoking the constructor, and the constructor itself can still validate invariants. This pattern avoids exposing multiple public constructors while retaining the safety of centralized initialization.
The this constructor initializer is a focused language feature that solves a specific duplication problem. It is most useful when you have multiple overloads that share setup logic. Used with restraint, it keeps your constructors small and your object invariants intact.