Constructor Execution Order
When creating a object in an inheritance hierarchy, the base class portion must be initialized first. This is achieved by implicitly invoking the base class’s parameterless constructor unless otherwise specified.
- Each class in the inheritance chain executes its base class constructor before running its own constructor body.
Important Notes
- Avoid calling virtual methods inside constructors.
- If a virtual method is called during base class construction, and it's overridden in a derived class, the derived implementation will execute—even though the derived class constructor hasn’t run yet.
- This means the virtual call reaches the derived class before its fields are fully initialized.
Constructor Initializer Syntax
By default, a derived class constructor invokes the base class’s parameterless constructor. However, if the base class defines multiple constructors, you can explicitly choose which one to invoke using a constructor initializer:
access_modifier ClassName(parameters) : initializer
A. Using base to Call a Base Constructor
The base keyword followed by arguments specifies which base constructor to use:
public DerivedClass(int x, string s) : base(s, x)
{
// constructor body
}
If no initializer is provided, the compiler implicitly adds : base().
Example
class Base
{
public string BaseField = "Base field";
public string DerivedStatus;
public Base(string status)
{
DerivedStatus = status;
}
public Base() { }
public void Print(string value)
{
Console.WriteLine($"Base method: {value}");
}
}
class Derived : Base
{
public string DerivedField = "Derived field";
public Derived() : base("Derived instance created")
{
}
public void PrintDerived(string value)
{
Console.WriteLine($"Derived method: {value}");
}
}
// Usage
static void Main()
{
var obj = new Derived();
Console.WriteLine(obj.DerivedStatus); // Output: Derived instance created
}
B. Using this to Delegate to Another Constructor in the Same Class
The this keyword allows one constructor to call another within the same class:
public MyClass(int x) : this(x, 1) { }
public MyClass(int x, int y) { }
Use Case 1: Shared Initialization Logic
When multiple constructors share common setup code, extract it into a private constructor:
class SharedInitExample
{
int value;
bool isNew = false;
public SharedInitExample() : this(true) { }
public SharedInitExample(int val) : this(true)
{
value = val;
}
private SharedInitExample(bool flag)
{
isNew = flag;
}
}
Note: You cannot move such initialization into a regular method if readonly fields are involved—they must be assigned directly in a constructor.
Use Case 2: Partial Initialization via Private Constructor
If some constructors don’t fully initialize an object, make the core constructor private and have public ones delegate to it:
class PartialInitExample
{
readonly int constantA;
readonly double constantB;
public string Name;
public int Id;
private PartialInitExample()
{
constantA = 20;
constantB = 30.5;
}
public PartialInitExample(string name) : this()
{
Name = name;
Id = -1;
}
public PartialInitExample(int id) : this()
{
Name = "Anonymous";
Id = id;
}
}
Class Access Modifiers
C# supports two primary access levels for top-level classes:
public
A public class is accessible from any assembly. Use this modifier to expose you're class outside its defining assembly.
internal
An internal class is only visible within the same assembly. This is the default accessibility level for classes—if no modifier is specified, the class is internal.