C# Constructor Execution Order
c# constructor execution order: Understand the exact order in which C# constructors run, including base class calls, field initializers, and static constructors, with...
When you create an instance of a class in C#, the runtime follows a strict sequence for invoking constructors. The order is not simply "from the most derived class down" or "from the base class up". Instead, it involves a mix of field initializers, base constructor calls, and the constructor body itself. Understanding c# constructor execution order helps you predict when side effects occur, debug initialization errors, and design classes that behave predictably under inheritance.
The core rule is: field initializers in the derived class run before the base constructor body, but after the base field initializers. More precisely, the sequence is:
- Static fields are initialized in the most derived class first, moving up the inheritance chain (but static constructors have their own timing, covered later).
- Instance field initializers in the derived class run before the base constructor call.
- The base constructor is invoked (including its field initializers and body).
- The derived constructor body runs after the base constructor returns.
Here's a minimal example that demonstrates the order:
public class Base { public Base() { Console.WriteLine("Base constructor body"); } } public class Derived : Base { private readonly int _value = LogAndReturn("Derived field initializer"); public Derived() { Console.WriteLine("Derived constructor body"); } private static int LogAndReturn(string message) { Console.WriteLine(message); return 1; } } // Creating an instance of Derived prints: // Derived field initializer // Base constructor body // Derived constructor body
The field initializer in Derived runs before the Base constructor is called. This is a common source of confusion because it feels like base initialization should happen first. The reason is that field initializers are injected into the constructor's start, and they must be executed before the base constructor call.
The Role of Base Constructor Arguments
When a derived constructor explicitly calls a base constructor using the : base(...) syntax, that call is placed after the derived class's field initializers. The order becomes:
- Derived field initializers run.
- The base constructor (with its own field initializers and body) executes.
- The derived constructor body runs.
Consider this example:
public class Base { public Base(string message) { Console.WriteLine($"Base: {message}"); } } public class Derived : Base { private readonly int _id = GetId(); public Derived() : base("from derived") { Console.WriteLine($"Derived id: {_id}"); } private static int GetId() { Console.WriteLine("Derived field initializer"); return 42; } }
Output when instantiating Derived:
Derived field initializer
Base: from derived
Derived id: 42
Notice that the argument to base(...) is evaluated after the field initializers, but before the base constructor body. If you have an expression like base(ComputeSomething()), ComputeSomething() runs after the derived field initializers. This timing is rarely important, but it becomes relevant when field initializers depend on external state that the base constructor would normally set up.
Field Initializers and Constructor Bodies: The Injection Point
The C# compiler transforms each constructor into a method where field initializers are inserted at the very beginning. If a class has multiple constructors, each one receives the same set of field initializers. That means a derived class with two constructors will run the field initializers twice (once for each constructor), which can lead to redundant work if the initializers are expensive.
public class Example { private readonly List<int> _data = new List<int>(); // Runs for every constructor public Example() { } public Example(int capacity) { _data.Capacity = capacity; } }
In the code above, new List<int>() is created before either constructor body runs. The second constructor then modifies _data.Capacity. This is safe because the field initializer ran first, but if the initializer throws, the constructor never executes.
A subtle consequence: field initializers cannot call virtual methods that rely on the derived class being fully initialized. Because the base constructor runs before the derived constructor body, a virtual call made from the base constructor will dispatch to the most derived override—but that override's fields may not have been initialized yet if the override is declared in the derived class. This is a known pitfall.
Static Constructors and Their Timing
Static constructors are a separate concern from instance constructors. A static constructor runs once per type, before any instance is created or any static member is accessed. The exact timing is implementation-dependent in the runtime, but the C# language guarantees that it runs at some point before the first use.
The order of static initialization is: static field initializers are executed in textual order, then the static constructor body. For derived classes, the static constructor of the base class runs before the derived class's static constructor, but only if the static members of the base are used or an instance is created.
public class Base { static Base() => Console.WriteLine("Base static ctor"); } public class Derived : Base { static Derived() => Console.WriteLine("Derived static ctor"); } // Accessing Derived triggers both, but order: // Base static ctor runs first, then Derived static ctor.
Do not rely on the exact point when a static constructor fires. If you need deterministic initialization, explicitly call a static method rather than depending on lazy static constructor timing.
Constructor Chaining: Using this(...)
Inside a class, you can chain constructors using this(...). When a constructor uses this(...), the field initializers still run first, then the target constructor body runs (after its own field initializers, which are also inserted). The chain then returns to the original constructor body.
public class Point { public int X { get; } public int Y { get; } public Point() : this(0, 0) { Console.WriteLine("Parameterless ctor"); } public Point(int x, int y) { X = x; Y = y; Console.WriteLine("Parameterized ctor"); } }
Instantiating new Point() prints:
Parameterized ctor
Parameterless ctor
The this chain runs the parameterized constructor first, then the parameterless one. This pattern avoids duplicated initialization logic, but note that any field initializers run once for each constructor, so they will execute twice in this scenario.
Virtual Calls During Construction
Calling a virtual method from a constructor is dangerous. When the base constructor runs, the derived constructor body has not executed yet, yet the virtual dispatch will call the most derived override. If that override depends on fields initialized in the derived constructor body, you will get null or default values.
public class Base { public Base() => Print(); public virtual void Print() => Console.WriteLine("Base"); } public class Derived : Base { private readonly string _name = "Derived"; public Derived() { } public override void Print() => Console.WriteLine(_name); }
Creating new Derived() prints null (or an empty string if _name were a string field without the initializer). The _name field initializer runs, but the override Print() is called from the base constructor—before the derived constructor body. The field initializer sets _name to "Derived", so in this case it actually prints Derived. However, if the derived class initialized _name in its constructor body instead, the base constructor call would see null. The safe pattern is to avoid virtual calls in constructors altogether.
When the Order Matters in Real Code
Consider a logging or tracing scenario where you want to record the construction sequence. The order described here directly affects the log output, so misinterpreting it leads to confusing diagnostics. Similarly, if a base constructor expects certain services to be set up by the derived class, you'll see flaky behavior. Always initialize dependencies in the base constructor itself or pass them via parameters.
Another practical case is object pooling. A pool might reuse instances by calling an internal Reset method. The constructor order never runs again, but the Reset method must replicate the same initialization steps to avoid stale state.
Edge Cases and Compatibility Notes
- Structs: A struct cannot have a parameterless instance constructor in older C# versions (before C# 10). Field initializers are not allowed in structs before C# 10 either. If you rely on constructor order for value types, the behavior differs.
- Records: Records use the same constructor order, but they also include synthesized members like
PrintMembersand equality members. The primary constructor runs in the order defined by the record declaration. - Partial classes: Field initializers are compiled in the order they appear in source across partial class files? No, partial classes merge all fields into one type; the order is based on how the compiler merges the files, but it is deterministic. It's better not to depend on cross-file ordering.
- Async constructors: You cannot have an
asyncconstructor. Use a factory method or anInitializeAsyncpattern. The constructor order is synchronous, so any async work must happen after construction.
Final Guidance on Predictable Initialization
To keep your code maintainable, follow these rules of thumb:
- Use field initializers for simple constants or defaults that do not depend on constructor parameters.
- Prefer passing required dependencies through the constructor parameter list over relying on field initializers that call external services.
- Avoid virtual calls in any constructor, including base constructors.
- If you need complex initialization, consider a static factory method that calls a private constructor and then performs additional setup.
- Remember that field initializers run for every constructor, so if you have multiple constructors, factor their common logic into a shared private constructor or a helper method.
Understanding the exact sequence—field initializers, base constructor, then derived body—lets you predict side effects and avoid subtle bugs. The ordering is consistent across all C# versions, but the surrounding features like static constructors and records have their own caveats. Keep the mental model simple: field initializers go first, then the chain of base constructors, then the constructor body you wrote.