Constructors
Constructors initialize object state at creation.
Introduction
Constructors initialize object state at creation. C# supports instance constructors, static constructors, primary constructors, and constructor chaining with : this(...) or : base(...). DI containers call constructors to inject dependencies in ASP.NET Core services.
Interviewers ask about constructor vs factory, static constructor thread safety, and record positional constructors. Avoid heavy work in constructors — prefer factory or async initialization patterns for I/O.
This lesson covers every constructor form with enterprise service examples.
The story
An invoicing microservice needs the current UTC time from an injectable clock (so unit tests can freeze time) and optionally a default currency for international clients. Constructor chaining lets one constructor delegate to another, and dependency injection supplies the clock automatically in ASP.NET Core.
Understanding the topic
Key concepts
- Constructor name matches class; no return type.
- Default parameterless constructor auto-generated if no other defined.
- this(...) chains to another constructor in same class.
- base(...) chains to parent constructor.
- Static constructor runs once before first static access.
- Primary constructor (C# 12) declares parameters on class line.
Step-by-step explanation
- new triggers allocation then constructor chain.
- Fields initialize in declaration order, then constructor body.
- Static constructor initializes static fields thread-safely once.
- DI resolves constructor with most parameters it can satisfy.
- Private constructor prevents external instantiation — singleton/factory.
- Records generate public positional constructor automatically.
Practical code example
Service with constructor injection and chained overload:
namespace TechLearningPro.Constructors;public interface IClock { DateTimeOffset UtcNow { get; } }public sealed class InvoiceService(IClock clock){private readonly IClock _clock = clock;public InvoiceService(IClock clock, string defaultCurrency): this(clock){DefaultCurrency = defaultCurrency;}public string DefaultCurrency { get; } = "USD";public Invoice Create(string customerId, decimal amount) =>new(Guid.NewGuid().ToString("N"), customerId, amount, _clock.UtcNow);public sealed record Invoice(string Id, string CustomerId, decimal Amount, DateTimeOffset CreatedAt);}
Line-by-line code explanation
public interface IClockabstracts time so tests can substitute a fake clock.public sealed class InvoiceService(IClock clock)uses a primary constructor to capture the clock dependency.private readonly IClock _clock = clockstores the injected clock in a readonly field.public InvoiceService(IClock clock, string defaultCurrency) : this(clock)chains to the primary constructor.: this(clock)runs the primary constructor logic before the secondary body executes.DefaultCurrency = defaultCurrencysets the currency on the overload that accepts it.public string DefaultCurrency { get; } = "USD"defaults to US dollars when not overridden.Create(string customerId, decimal amount)builds a new invoice using the injected clock.Guid.NewGuid().ToString("N")generates a compact unique invoice ID.sealed record Invoice(...)is an immutable snapshot of the created invoice.
Key takeaway: Primary constructor captures IClock. Secondary constructor chains with : this(clock). DI container uses public constructor with resolvable parameters.
Real-world use
Where you'll use this in production
- ASP.NET Core services receiving ILogger, DbContext via constructor.
- Immutable domain objects requiring all fields at creation.
- EF Core entities with parameterless ctor for materialization.
- Singleton with lazy static initialization.
Best practices
- Inject dependencies via constructor — explicit, testable.
- Validate arguments in constructor; throw ArgumentNullException.
- Keep constructors lightweight — no I/O or blocking.
- Use factory methods when construction logic complex.
- Provide parameterless private/protected ctor for serializers/EF.
Common mistakes
- Service locator inside constructor anti-pattern.
- Calling virtual methods from constructor — fragile inheritance.
- Forgetting to chain base(...) in derived class.
- Two many constructor overloads — use options record.
Advanced interview questions
Q1BeginnerConstructor chaining this vs base?
Q2BeginnerWhen static constructor runs?
Q3IntermediateWhy primary constructors?
Q4IntermediateConstructor injection benefits?
Q5AdvancedEF Core requires parameterless constructor — reconcile with DI entity?
Summary
Constructors initialize state; chain with this/base. Primary constructors and DI are modern enterprise defaults. Validate inputs; avoid heavy work in constructors. Static constructors run once for static initialization. Next: encapsulation and information hiding.