Constructor
“What is a constructor, and why validate inside it?”
The code that runs once, the moment an object is built. The one place that can refuse a bad object.
See it
new Loan(500000, 0, 60) asks for a loan with rate 0, say from a typo in a form.In plain words
A constructor is the code that runs once, the moment builds an object. Its job is to give the object its starting values, and to refuse bad ones. If it throws an error, the object is never handed to anyone, so a broken Loan can never exist.
- A constructor is a special method with the class's name and no return type.
- The runtime allocates the object with every field zeroed, then runs the constructor to initialise it.
- Validating there enforces the class's invariants, the rules that must always hold, like rate above 0: if an input is invalid it throws, never returns a , and no code can ever hold an invalid instance.
- If you declare no constructor, the compiler adds a public parameterless one.
- Order of execution: field initializers run first, then the base class constructor, then this constructor body.
In the lending API
The same file before and after the fix, plus its test. Press Next line to walk through them.
Lending.sln · where these 3 files sit
- src/Domain/Servicing/
- tests/Domain.UnitTests/Servicing/
What they ask next
Interviewers check that you can explain every line. Tap a question to open it.
Explain this lineWhy throw, and not fix the value or return false?
A constructor has no return value, so throwing is the only way to say no. Quietly turning 0 into a default hides the typo, and the customer gets a loan nobody asked for.
Why not…Why not check in the controller instead?
Do both. The API checks input first, so the user gets a clear message. The constructor is the last guard: every path that builds a Loan goes through it, including an import job, a test, or a screen added next year.
What does the user see if the constructor throws?
If nothing catches it, a 500 error. Teams catch these errors in one place in the and turn them into a 400 with the message.
Why not…Why not the .NET 8 one-line check helpers?
They are a good choice. has static helpers, like
ThrowIfNegativeOrZero(rate): one line per value, and the message names the parameter and the bad value. Many teams prefer it. Either way, the check runs before the object can be used. If you switch, the test must expect , or use .What is constructor chaining?
One constructor calling another with , so the checks live in one place.
public Loan(decimal amount, int months) : this(amount, 9.5m, months) { }runs the main constructor first. calls the parent class's constructor instead.What is a static constructor?
It runs once per class, before the class is first used, to set up data. No parameters, no , and you never call it yourself. If it throws, the class stays broken until the app restarts ().
What is a private constructor for?
It stops outside code from calling . Used for a factory method (a static method that builds the object for you) like , which can return an error instead of throwing; for a class with only static members; and for a singleton, a class with exactly one shared object (see ).
What happens if you write no constructor?
The compiler adds a public one with no parameters, so
new Loan()works. Write any constructor yourself and that free one is gone.In what order do the parts run?
Static parts first, once per class: static field initializers, then the static constructor. Then for each : this class’s field initializers, then the base class constructor (its initializers, then its body), then this constructor’s body.
Does EF Core call this constructor when it loads a loan?
It can. fills constructor parameters whose names match properties (amount, rate, months) and sets the rest, like Id, itself. Some teams add a private constructor with no parameters for EF, so loading old rows never runs today's checks.
What is a primary constructor?
C# 12: parameters written on the class line,
public class LoanService(ILoanRepository repo). They can be used anywhere in the class. Common for services that get their parts from the . There is no body, so checks must go into property initializers, which gets clumsy; classes like Loan keep a normal constructor. Note: the parameters are not readonly fields, so any method can reassign them.Can a constructor be async?
No. If setup needs a database or web call, use a static method that awaits the call and then calls . Constructors should be quick and never wait on I/O.
What about
new Loan { Rate = 0 }?That is an object initializer. It sets properties after the constructor has finished, so the constructor can't check those values. That is one reason Loan uses and takes every value through the constructor. C# 11
requiredmakes the compiler insist a property is set, but it still can’t check the value.
Traps
- Don't say
The constructor creates the object.Say makes the object, with every value at 0. The constructor then sets it up. - Don't say
If I don't write a constructor, the class has none.SayThe compiler adds a public one with no parameters. Write any constructor yourself and that one is gone. - Don't say
Load the data from the database in the constructor.SayConstructors can't be async and should be quick. Load data in an async method instead.
Canva's interview guide says it grades whether you find and fix the issues in AI-written code and make it production-standard. A class that accepts any value is the kind of gap they mean. Before you accept generated code, ask: what values can reach this object, and what breaks later if a bad one does?