← Home
.NETOOP and C# basicsReported by candidates

What are static, sealed and partial classes, and when do you use each?

Asked in first technical rounds at Infosys, EbixCash (twice), HCLTech, Accenture and six other companies; latest report September 2026.

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

Invented to show the shape, in C#. Tap Next to walk a 1,000 quote through .

UDC.sln · where these 2 files sit
  • Quotes/
  • Common/
Diagram · step 1The quote calls on the class itself. No object is created.
QuoteService.cs · line 1 · 1 of 2Inside , called by the quote controller for one UDC customer.

Fix later in your repo The rate is a constant; if NZ changes , it would move to config and a DI service.

Say it out loud, then check

Show the 30-second answer
  • A static class can't be created with new and holds only static members: one copy for the whole app. I use it for stateless helpers, constants and extension methods, never for data that changes per request, because every request shares it.
  • A sealed class can't be inherited. I seal by default, so nobody overrides behaviour that wasn't designed for it.
  • A partial class is one class split across files that the compiler joins at build time. It's for generated code: EF Core scaffolding writes one file, and I write mine in another, so regenerating never wipes my code.
Check your answer: tick what you said

You said 0 of 7

Then add your example: UDC Finance GST helper (invented example)
  • Situation: at UDC Finance every quote adds New Zealand , a 15% tax. Three services each had their own copy of the maths, and one stored the last quote in a static field "for logging".
  • What I did: one static class, , with the rate as a constant and one method, . It keeps no data that changes. In review I removed the static field: it was shared by every request.
  • Result: one place for the tax rule, nothing to create or inject, and no shared state. If the rate ever came from config, I'd make it a normal class in DI.
  • What I'd say: static is right for pure functions and constants; wrong for anything that holds per-request data.

See it

1/6Before: EF Core wrote from the database. A developer added a method inside that file.

In plain words

The senior answer · about 2 minutes
Static
  • A static class is for code that needs no object and keeps no data that changes: rounding money, a tax rate constant, extension methods, which add a method to an existing type without changing it, like services.AddLending() that registers all our services in one call. The compiler won't let you write on it, and every member must be static. The walks the difference between static and instance members.
  • The danger isn't the class, it's a static field that changes. There is one copy for the whole process, so every request and every thread shares it. If two requests write it at the same time, one overwrites the other. That's the try-first bug below.
  • Static also means hard to replace. A static class can't implement an interface or be injected, so a test can't swap it for a fake. If it talks to a database, the clock or an HTTP API, I make it a normal class registered in DI instead, often as a singleton.
Sealed
  • A sealed class can't be a base class; trying gives a compile error. I seal by default, because a class that allows inheritance must be designed for it: which methods are safe to override and in what order they run. Unsealing later is easy; sealing later breaks anyone who inherited.
  • There's a small speed gain too: when you call a virtual or interface method through a sealed type, .NET knows no override can exist and can call it directly instead of looking it up. string is sealed for both reasons: nobody can change how a string behaves. The has the details, including sealing one override.
Partial
  • A partial class is one class written in several files. Every part says partial, they must be in the same project and namespace, and the compiler merges them into one class before anything runs. At run time there's no trace of the split.
  • I use it where a tool owns one half. EF Core's scaffold command generates entity classes from the database and overwrites them every time we rerun it. Our own methods go in a second file with the same partial class, so a regenerate never deletes them. Source generators, like the logging and regex ones in .NET, use the same trick.
  • I don't use partial to split a big hand-written class into files. A class that big has too many jobs; splitting the file hides that instead of fixing it.

All in one flow

Coming Not written yet: the card pass adds it.

The story

  1. Before

    UDC Finance's lending API gets its entity classes from EF Core's scaffold command: it reads the tables and writes one C# class per table. A developer needed an "is this contract in arrears?" check and added straight into the generated .

  2. Why it had to change

    Weeks later a added a column for the fee. Someone reran scaffold with --force, which rewrites every generated file. vanished, and the build broke in every place that called it. Worse when it doesn't break: a changed rule inside the file is silently reset.

  3. How it works now

    EF Core marks every class it writes as partial. So our method moves to a second file, , that also says partial class Contract. Scaffold only rewrites its own file. When the project builds, the compiler merges both parts into one class, so the rest of the code never knows there are two files. The diagram walks it.

  4. What happens next

    The now uses all three kinds. is partial. is sealed, so nobody subclasses it to change how a is calculated. is a static class: a rounding helper that keeps no data, called without creating anything.

  5. What we gained

    Regenerating is safe. Behaviour can't be changed by a surprise subclass. And the only static code is code with nothing to share. The one rule that needs care is static fields, which is the try-first bug below.

In the lending API

Our half of the entity. EF Core's half is in the next file; the compiler joins them.

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

Lending.sln · where this file sits
  • src/Infrastructure/Data/Entities/
⋯ 1 line in Deep⋯ 1 line in Deep
Contract.Custom.cs · line 2 · 1 of 2Same name, same namespace, same project, and partial on every part. Leave it off one part and the build fails (CS0260, missing partial modifier).

The code, file by file

A UDC Finance in the lending API: an EF Core entity in two parts, a static helper and a sealed service. Tap Next to walk the diagram with the code.

Lending.sln · where these 4 files sit
  • src/
    • Infrastructure/Data/Entities/
    • Application/
      • Quotes/
      • Common/
Diagram · step 1Before: EF Core wrote from the database. A developer added a method inside that file.
Contract.cs · line 1 · 1 of 3This comment tells people, and tools, that a generator owns this file. Edits here are lost on the next scaffold.

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, and how to tell them

Each one: what you see, why, the fix in code, and the line to say.

Production incident

Not on this card A basic concept card: its production story is the try-first static-field race here; thread races in depth are on the Task vs Thread card.

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 · 10 minutes

Write a thread-safe singleton in C#: one object for the whole app, created only when first used. Then say when you'd use a DI singleton instead.

Publicis Sapient, tech round, April 2026: singleton implementation
Stuck? Hints
  1. How do you stop other code writing new RateTable()? (a private constructor)
  2. is a .NET type that creates its value once, the first time you ask, and is safe when two threads ask together.
  3. Why mark the class ?
Show a solution
Lending.sln · where this file sits
RateTable.cs · line 1 · 1 of 5 says nobody should extend this. The private constructor already blocks outside subclasses; sealing also blocks a nested one.
What the interviewer checks
  • Private constructor, so fails outside the class.
  • static readonly Lazy<RateTable>: created on first use, thread-safe by default.
  • Class is : it states the intent, and closes the last gap, a nested class inheriting it.
  • No hand-written (the older double-checked locking pattern); does it for you.
  • Says the DI answer: AddSingleton<IRateTable, RateTable>() gives one instance plus an interface, so tests can fake it.
With AI tools

AI assistants like to make helpers static and to cache things in static fields, because it's short and it compiles. In review I check every field that isn't or const: who writes it, and can two requests write it at once? I also check that nobody, human or AI, edited a file marked <auto-generated/>; that edit is lost on the next scaffold and belongs in the partial file.

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