← Home
.NETOOP and C# basicsReported by candidates

What is the difference between a class and an object, and what types of constructors does C# have?

16 reports in the index, from Infosys, Capgemini, Zobi, Sdaemon, Ishttaa and others; constructor types and the static-versus-default order repeat most.

Say this first · about 30 seconds
Make it yours · UDC Finance quote import (invented example)
The code, simplified

Invented to show the shape, in C#. Tap Next to walk a quote from first use to the chained constructor.

UDC.sln · where this file sits
  • Quotes/
LoanQuote.cs · line 3 · 1 of 7private static readonly = one value shared by the type, set once and never changed.

Fix later in your repo The batch, dates and counts are invented. The code shape matches quote origination at UDC: one constructor owns validation, and the short overload chains to it.

Say it out loud, then check

Show the 30-second answer
  • A class is a type, written once. An object is one instance (one filled-in copy) made from it at run time, with its own copy of the data.
  • An instance constructor has the class name and no return type; it runs for every new object and leaves it valid.
  • Types: parameterless, the default constructor that C# adds only when you write no instance constructor; parameterized and overloaded (several constructors with different parameters); static; and private. A copy constructor is one you write.
  • A static constructor runs once per type, before the first object or static member (a field or method marked static, shared by the type) of that class is used, and cannot be called by name. So it runs before the parameterless instance constructor, always, except when a static field initializer (a value written on the field itself) creates the class itself.
  • Then for each object: instance field initializers, then the constructor body. With a parent class (the base class it inherits from), the parent's constructor runs after the child's field initializers.
  • Chaining: a constructor can call another constructor of the same class first, or the parent's constructor. A private constructor blocks outside creation and pairs with a factory (a static method that builds the object for you).
Check your answer: tick what you said

You said 0 of 9

Then add your example: UDC Finance quote import (invented example)
  • Situation: at UDC Finance, quote data came in from a dealer spreadsheet. A new adviser imported a batch with a zero tax rate. Every quote was created, saved, and only failed later when the repayment schedule was calculated.
  • What I did: the quote class had a public parameterless constructor and public setters (methods that assign a field), so any state was allowed. I replaced it with a constructor that takes the amount, rate and term, checks them, and stores them in get-only properties. A short overload chains to it with the standard rate.
  • Result: an invalid quote cannot be created. The import either produces a valid quote or fails on the row, with the constructor naming the bad value. The check runs once and no later method has to defend against a zero rate.
  • What I'd say: constructors are where a class turns input into a valid object. If an object can exist in a broken state, every method that touches it needs a guard, and one of them will forget.

See it

1/9A class is written once: says what every loan object holds and can do.

In plain words

The senior answer · about 2 minutes
Class and object
  • A class is a compile-time type: the compiler checks every use against it. An object is a run-time instance with its own fields. is the class; each new Loan(...) is a separate object.
  • A variable of a class type holds a reference, not the object. Two variables can point at one object, and assigning one to the other copies the reference. are the usual follow-up.
The constructor types
  • Instance constructors: parameterless, parameterized and overloaded. The parameterless one is only compiler-provided if the class declares no instance constructor; a static constructor does not count.
  • A static constructor initializes the type itself: static fields, a cache, a one-time read. It runs at most once per process, before the first static or instance member is used, and takes no arguments.
  • A private constructor blocks outside the class: a factory that returns a cached instance, or a singleton. For a helper with only static members, prefer a , which the compiler enforces; older code uses a private constructor for the same job.
  • A copy constructor is not a C# feature; it is an ordinary constructor that takes the same type and copies the fields. With reference fields, decide whether the copy is shallow or deep.
Order, exactly
  • For one class on first use: static field initializers in order, then the static constructor body. Once only. If it throws, the runtime wraps it in (the error .NET throws when a type's static setup failed), and the type stays unusable for the life of the process.
  • For each object: instance field initializers, then the base constructor, then this constructor's body. In a derived class the child's field initializers run before the base constructor.
  • With this(...), the chained constructor runs first. Field initializers run in the constructor that ultimately calls base(...), not twice.
  • allocates, runs the constructor, and returns the reference. If the constructor throws, no reference is returned and the object is garbage.
In production
  • Validate in the constructor so an invalid object cannot exist. Then every later method can trust its fields, and the failure points at the row that supplied the bad values.
  • Do not put I/O, a network call or a long read in a static constructor. It runs once, its failure is cached, and there is no retry until the process restarts. Build the object in the container at start-up instead, where a failure stops the deploy.
  • Keep constructors short and side-effect free apart from assigning fields. A constructor that sends email or writes to the database cannot be tested in isolation and runs again in every test that builds the object.

One object and two constructors

Follow one loan from the first to a rejected rate, and watch which constructor runs when.

1/8A class is the type: written once, it declares what every loan object holds.

The story

  1. Before

    Before this, a quote could be created empty, saved, and given a rate later. A typo or a skipped field produced a row that looked fine until a repayment schedule divided by a zero rate. Nobody could say where the bad state entered, because every setter accepted anything.

In the lending API

The key file: the static part runs once, the checks run for every object, and the overload chains to them.

Only the key lines are shown; Deep walks the whole file.

Lending.sln · where this file sits
  • src/Domain/Servicing/
⋯ 9 lines in Deep⋯ 2 lines in Deep⋯ 9 lines in Deep
Loan.cs · line 7 · 1 of 3Runs once per process, before the first object; the static fields are already set. No parameters, and never called by name.

The code, file by file

Two files and a caller, .NET 8. Tap Next to follow the first , a second object, a chained overload, a private constructor and a rejected rate.

Lending.sln · where these 3 files sit
  • src/
    • Api/
    • Domain/Servicing/
Diagram · step 1A class is written once: says what every loan object holds and can do.
Program.cs · line 3 · 1 of 5First use of : the type is readied before this call returns (see Loan.cs). One loan, 500,000 at 9.5% for 60 months. The m suffix marks a decimal literal; the underscore is only a digit separator.

What they ask next

The follow-ups interviews really ask. Answer out loud, then tap to check.

Follow-up tree

Answer, then open the next level. That is the order an interviewer digs in.

Reporteda candidate was asked Intervieweran interviewer asks it

Production bugs

Coming Not written yet: the card pass adds it.

Production incident: NZ quotes failed on one instance after a rate-file rename

P2 · serious, has a workaroundUDC Financea wrong settingNZ quote API on App Service

Composed, not a real event at UDC Finance. The mechanism is real .NET: a static constructor runs once per process, and if it throws the runtime wraps the error in `TypeInitializationException` and keeps the type unusable until the process restarts. Dates, counts, names and ticket numbers are composed.

The story in 5 lines
  1. On 30 Sep one of the three quote API instances (separate copies of the app running side by side) started returning 500 for every NZ quote. The other two were fine, so the dealer desk saw failures on roughly one request in three.
  2. The deploy had renamed the rate file and changed the setting, but still read a hard-coded nz-rates.json in its static constructor.
  3. The file was missing, the static constructor threw, and .NET cached the failure: every later use threw (the error .NET throws when a type's static setup failed) without retrying the file.
  4. Fix: make the table a normal singleton (one shared object for the whole app), built by the container (the .NET object that builds and hands out shared services) at start-up, and let a bad file stop the deploy instead of the first quote.
  5. Prevent: a start-up test that reads the rate file, and a 5xx alert (server errors, 500–599) per instance, not only for the whole service.
Who's who (4 people)
You
.NET developer on the vendor team. Finds the cause and writes the fix.
Tech lead
Vendor senior. Reviews the change, runs the postmortem (the review held after an incident).
L2 pod
Vendor support. Takes the ticket, runs the logs query.
UDC Finance operations lead
Owns the dealer quote desk. Decides how the failed quotes are recovered.

Tap a step. The map lights the place where it happens.

1/4 Detect

Detect: one of three instances returns 500 for every NZ quote. The other two are fine.

Wed 30 Sep, 09:10 NZDTUDC Finance operations lead, then the L2 pod

  • What: the dealer desk reports that NZ quotes fail with a 500, but only some of the time. A retry sometimes works.
  • Why it looked random: (Azure's managed web hosting) has three instances behind a load balancer (spreads requests across instances). The 08:55 deploy replaced one instance at a time: App_0 restarted into the new package, which held only nz-rates-v2.json, while the other two instances still ran the old code, already loaded in memory. Requests that landed on App_0 failed; the other two served normally.
  • How we saw it: filtered the exceptions (error rows from the app) by instance. Every 500 was on App_0, and every message held with inside.
App Insights (Azure's log store): errors by instance
// KQL = App Insights query language.
// exceptions rows are errors.
// cloud_RoleInstance names the instance.
exceptions
| where timestamp between (
    datetime(2026-09-29) ..
    datetime(2026-10-01))
| where outerMessage has "NzRateTable"
| summarize count() by cloud_RoleInstance,
    bin(timestamp, 10m)
In theory

A 5xx alert per instance: page when one instance's failure rate passes 5%, even if the service average looks healthy.

In practice

The service-level alert watched the whole app. One bad instance was a third of traffic, below the 50% threshold, so nobody was paged.

Try it yourself · 10 minutes, on your Mac
  1. dotnet new console -n StaticLab. Add with a static constructor that reads a file that does not exist.
  2. Call twice, each in a try/catch. The first call throws ; so does the second, without trying the file again.
  3. Move the read into an instance constructor and call it at start-up. A missing file now fails once, at start-up, with in the inner exception.
  4. Add a unit test that starts the app with the file missing and expects start-up to fail.
You should see

A static constructor runs once. If it throws, the type stays broken for the life of the process.

The senior move

"A static constructor is a one-time initializer with no retry: if it does I/O and throws, the whole type is dead until restart. I load shared state in the container at start-up, so a bad file fails the deploy instead of a customer request."

Sources Sourced: static constructors run once per type before first use; an exception there becomes , the type cannot be used again in that process, and the real error is in (C# programming guide and .NET 8 runtime behaviour). Composed: UDC Finance's systems, dates, instance counts, names, ticket and numbers, and the (standard columns).

Traps

Traps

What should you say instead? Tap to check.

Live coding

Easy first, then harder. Set a timer and type it before you look.

Task 1

Reportedeasy · 15 minutes

Write a class with two constructors: one takes amount and rate, the other takes amount and uses a standard 9.5% rate. Both must reject a non-positive amount or rate, and both must produce an object with get-only properties (set only inside the constructor).

Infosys and Zobi (constructor types)
Stuck? Hints
  1. Put the checks in one constructor and make the other call it with this(...).
  2. Get-only properties can be assigned in the constructor body.
Show a solution
Lending.sln · where this file sits
What the interviewer checks
  • The overload chains with this(...) instead of repeating the checks.
  • Both paths reject a zero amount and a zero rate.
  • No public setters: an invalid object cannot be created after construction.

Task 2

Question index: Mustmedium · 20 minutes

Predict the exact output of new Child() in the code below, then explain why the order is not parent first.

Sdaemon (static versus default constructor)
Stuck? Hints
  1. Each type has its own static constructor, triggered by first use of that type.
  2. The child's field initializer runs before the base constructor is reached.
Show a solution
Lending.sln · where this file sits
Program.cs · line 1 · 1 of 2Top-level statement: the program runs this line first. _ = throws the result away; => is a one-line body.
What the interviewer checks
  • C static is first, then C field, then P static, then P body, then C body.
  • Explains that the child type is readied first, then its field initializer, then the parent type and constructor.
  • Knows a static constructor runs once per type, not once per hierarchy.

Task 3

Question index: Musthard · 25 minutes

Write a copy constructor for that deep-copies a mutable , so changing a charge on the copy does not change the original.

GQS (private constructor and implications); copy-constructor drill derived from the index
Stuck? Hints
  1. A new list alone is not enough: the same charge objects are still shared.
  2. Copy each mutable charge into a new object inside the loop.
Show a solution
Lending.sln · where this file sits
What the interviewer checks
  • Creates a new list, not a copy of the list reference.
  • Creates a new for every element, copying its fields.
  • A test changes the copy's first charge and asserts the original is unchanged.
  • Can explain when a shallow copy would be safe: when the elements are immutable.
With AI tools

AI assistants often generate the easiest shape: a parameterless constructor plus public setters, which lets an invalid object exist. For example, before: new Loan() then loan.Rate = 0;; after: new Loan(500_000m, 9.5m, 60), which rejects a zero rate. In review I ask for the constructor that makes the invalid state impossible, then ask for a test that tries to create it. The same review catches a static constructor doing I/O: it runs once, and its failure is cached for the life of the process.

Glossary: every name, one line each →

My notes

Paste the NOTES block from your doubt chat here. The grader never reads notes; the coach reads "Still unclear".

Read moreWhere it was asked