Top C# and .NET Interview Questions with Answers

C Sharp interview questions test how well a .NET candidate understands the type system, memory management, asynchronous code and ASP.NET Core architecture. I have managed web projects since 2012, and I ask these questions from the other side of the table when I hire back end developers. In this guide I share the questions I see most often, grouped by topic, with short answers.
The list moves from junior to senior level. First come core language behaviours, then collections and async programming, and finally ASP.NET Core, Entity Framework Core and system design. For each question I also explain what the interviewer is really listening for. In short, you prepare the reasoning, not only the answer. None of this is a hiring guarantee; it is a study framework based on my own field experience. You can find related articles in the software category.
What are the most common C Sharp interview questions?
C Sharp interview questions cover five main areas: the type system and memory, object oriented design, collections and LINQ, async and await, and dependency injection in ASP.NET Core. As seniority grows, the questions move away from definitions. Instead, they focus on performance, debugging and the reasons behind architectural choices.
When you prepare, answer a few questions from each area in your own words. Then write a short snippet and explain out loud why it works. Interviewers usually care more about your thinking order than about a perfect answer. So saying "I am not sure, but here is how I would reason about it" beats a made up answer every time.
- Junior level: value and reference types, string behaviour, access modifiers.
- Mid level: LINQ, IEnumerable versus IQueryable, async and await, exception handling.
- Senior level: garbage collection, Span, service lifetimes, performance profiling.
Why do C Sharp interview questions include .NET versions?
This question checks whether you follow the ecosystem. According to the official .NET support policy, Microsoft ships a new major version every November. Even numbered releases are LTS and get three years of free support. Odd numbered releases are STS and get two years.
The same page lists .NET 10 as an LTS release supported until November 2028. Meanwhile, both .NET 8 and .NET 9 reach end of support in November 2026. Therefore a good answer to "which version would you start a new project on?" goes beyond a number. It also weighs the support calendar and the cost of future upgrades.
You should also explain the difference between .NET Framework and modern .NET. Framework runs only on Windows and receives maintenance fixes. Modern .NET, on the other hand, is cross platform and runs happily in Linux containers.
What is the difference between value types and reference types?
This is a classic opener, so expect it early. Value types such as int, double, bool, struct and enum hold their data directly. When you assign one variable to another, you get a copy. Reference types such as class, string, array and delegate hold a reference to an object. After an assignment, both variables point to the same object.
A common mistake is to say "value types live on the stack and reference types live on the heap" as a hard rule. However, an int field inside a class lives on the heap together with that class. The Microsoft documentation on value types also defines the split by copy semantics, not by location.
For example, Point p2 = p1; p2.X = 5; leaves p1 unchanged when Point is a struct. With a class, p1 changes too. Write this tiny example on the whiteboard and the question is closed.
How does boxing affect performance?
Boxing wraps a value type in an object on the heap when you assign it to object or to an interface. Unboxing then takes the value back out. Every boxing operation allocates, so it adds pressure on the garbage collector.
For instance, adding an int to the old ArrayList boxes every element. A generic List<int> removes that cost. That is why interviewers often link this topic to "why do generics exist?" A strong answer ends with measurement: if you suspect boxing in a hot loop, you profile allocations with BenchmarkDotNet instead of guessing.
Why is string immutable and when do you need StringBuilder?
In C#, string is an immutable reference type. When you concatenate, the existing object stays the same and a new one appears. In practice, this design helps with thread safety and string interning.
On the other hand, hundreds of concatenations inside a loop create hundreds of objects. In that case StringBuilder works on a single buffer and cuts allocations sharply. Still, using StringBuilder for two or three joins only makes the code longer. The compiler already handles simple concatenation well.
Next, interviewers usually ask about == versus Equals. For string, both compare content, because string overloads the == operator. For most other classes, == compares references unless the type overrides it.
How do you choose between an abstract class and an interface?
This question tests your object oriented judgement. An abstract class can carry shared state and shared behaviour. However, a class can inherit from only one base class. An interface defines a contract, and a class can implement many interfaces.
Default interface methods, added in C# 8, blurred the line a little. Still, the practical rule holds:
- Pick an abstract class when objects share an "is a" relationship and common state.
- Pick an interface when you want to give unrelated classes a shared capability.
- Pick an interface when testability and dependency injection matter most.
In other words, the interviewer wants reasons, not definitions. An example from your own project makes the answer much stronger.
How do the SOLID principles show up in C# code?
Listing five letters is not enough. You need to tie each principle to a concrete code decision. For example, single responsibility means an order service does not send emails. Open closed means you add a new payment method by adding a class, not by editing a switch block.
For Liskov, the classic example is the square that inherits from a rectangle. Interface segregation prefers small, focused interfaces over one giant IRepository. Finally, dependency inversion maps directly to the built in dependency injection of ASP.NET Core.
Honesty earns points here too. Applying every principle everywhere leads to over abstraction. A five layer architecture for a tiny service raises maintenance cost. A candidate who can say that sounds experienced.
What is the difference between IEnumerable and IQueryable?
In practice, every team that uses Entity Framework Core asks this one. IEnumerable works on an in memory collection, so filters run in C#. IQueryable carries an expression tree, and the query provider translates that tree into SQL.
The practical result matters. If you call ToList() early on a DbSet, you pull the whole table into memory and filter afterwards. If you write Where before ToList(), the filter runs in the database. As a result, one line in the wrong order can slow down production badly.
| Aspect | IEnumerable | IQueryable |
|---|---|---|
| Runs in | Application memory | Data source, for example SQL |
| Filtering | C# delegates | Expression tree translated to SQL |
| Best for | In memory lists | Database queries |
| Typical risk | Loading too much data | Expressions the provider cannot translate |
What does deferred execution mean in LINQ?
A LINQ query does not run until you consume the result. Writing Where or Select only builds a recipe. Specifically, the query runs when foreach, ToList() or Count() arrives.
Interviewers like this trap: if you loop over the same IEnumerable twice, the query runs twice. Against a database, that means two separate SQL calls. So if you need the result more than once, you materialise it with ToList() one time.
You should also know how closures behave. An outer variable inside the query takes its value at execution time, not at definition time. That detail also explains many confusing bugs.
How do async and await actually work?
async and await let you continue work without blocking a thread. The compiler turns an async method into a state machine. At an await, if the task has not finished, the method returns to its caller. When the task completes, the rest of the method resumes.
The most common misunderstanding is "async starts a new thread". In fact, during I/O work such as an HTTP call or a database query, no thread sits waiting. The Microsoft guide to asynchronous programming explains this through I/O bound and CPU bound work.
Then the interviewer asks about common mistakes:
- Calling .Result or .Wait(): this can deadlock where a synchronisation context exists.
- Writing async void: you cannot catch its exceptions, so keep it for event handlers.
- Expecting await to speed up CPU bound work: that work needs Task.Run.
What is the difference between Task and Thread?
A thread is an operating system unit of execution and it is expensive to create. A Task is an abstraction for "work that will finish later". It usually runs on the thread pool, and sometimes it uses no thread at all. That is why modern C# code rarely creates threads by hand.
Next comes ValueTask. It reduces allocations on hot paths where the result is often ready synchronously. However, you cannot await it twice, so Task stays the default. Also prepare for CancellationToken questions. Passing a token to long async methods stops the server from working for a client that already left.
How does thread safety work in .NET?
This question often starts with a small counter. Two threads increment the same int. So why is the final number too low? Because an increment reads, adds and writes. Another thread can slip in between those steps. We call that a race condition.
Solutions follow a cost ladder. For simple counters, Interlocked.Increment is enough. When you change several fields together, you use a lock block. For shared dictionaries, ConcurrentDictionary gives you a ready structure.
However, you cannot await inside a lock, because the compiler refuses it. In async code you use SemaphoreSlim with WaitAsync instead. Interviewers notice this detail at once, since it shows you have fixed a real locking issue in an async service.
What are Span and Memory for?
Span<T> lets you slice an array, a string or stack memory without copying. For example, Substring creates a new string, while AsSpan creates no allocation at all. Because Span is a ref struct, it lives only on the stack. So it cannot be a class field and cannot cross an await. Memory<T> solves that, since it can live on the heap and turn into a Span when needed.
How do garbage collection and IDisposable work together?
The .NET garbage collector cleans managed memory and sorts objects into generations 0, 1 and 2. Short lived objects die quickly in generation 0. Survivors then move up. Large objects go to a separate large object heap. You can read the details in the garbage collection fundamentals page.
Still, the GC only knows about memory. File handles, database connections and sockets are unmanaged resources, and you release them yourself. IDisposable and the using statement do this job. A using block guarantees a Dispose call when the block ends.
Beyond that, a good answer mentions finalizers. A finalizer belongs only in classes that hold an unmanaged resource directly, and it delays collection. That is the reason you call GC.SuppressFinalize inside Dispose.
What is the difference between Singleton, Scoped and Transient?
ASP.NET Core ships with a built in dependency injection container. When you register a service, you choose one of three lifetimes. The Microsoft dependency injection docs describe them like this:
- Transient: you get a new instance every time you ask.
- Scoped: one instance lives for each HTTP request.
- Singleton: one instance lives for the whole application.
The real question follows. What happens if you inject a Scoped DbContext into a Singleton? We call this a captive dependency. The DbContext stays stuck in the first scope, and since it is not thread safe, it breaks under concurrent requests. In development, scope validation catches this at startup.
How does the ASP.NET Core middleware pipeline work?
Middleware is a chain of components that every HTTP request passes through in order. Each component handles the request, passes it on, or short circuits and returns a response itself. Then, on the way back, the response travels the chain in reverse.
Order therefore matters a lot, because each step sees only what came before. For example, UseAuthentication must come before UseAuthorization. Otherwise authorization runs without knowing who the user is. The exception handler should sit near the top, so it can catch errors from everything below it.
Sometimes the interviewer asks you to write a custom middleware. A component that measures request time and logs it is a good example. If you clearly separate the code before and after await next(context), you show that you understand the flow.
How do you fix the N+1 problem in Entity Framework Core?
The N+1 problem appears when you run one query for a list and then one extra query per item for related data. For example, a list of a hundred orders turns into a hundred and one queries. It also tends to hide behind lazy loading.
You have several fixes. Use Include to load related data up front. Alternatively, use Select to project only the fields you need into a DTO. For large joins, AsSplitQuery can help. On read only screens, AsNoTracking also removes change tracking overhead.
Above all, the most convincing answer starts with diagnosis. Explain that you read the generated SQL from the logs and counted the queries. Then the interviewer knows you solved a real production problem, not a textbook one.
Which exception handling mistakes should you avoid?
This question quickly separates people who have shipped code. First rule: do not catch what you cannot handle. An empty catch block hides the error, and the problem returns later in a far more expensive place.
The second rule is about rethrowing. Inside a catch, throw ex; resets the stack trace, while throw; keeps the original one. That small difference saves hours when you chase a bug in production at midnight.
Also, do not use exceptions for flow control. If user input is invalid, return a result object or a validation error. That is cheaper and easier to read. In ASP.NET Core, a central exception handler with ProblemDetails responses gives API consumers a consistent contract.
- Catch the most specific exception type you can.
- Log context with the message: user, request id and input.
- Release resources in every case with finally or using.
How do you write unit tests in a C# project?
Interviewers often phrase it as "how would you test this code?" xUnit, NUnit and MSTest are the common frameworks. The structure of your test matters more than the brand. The Arrange, Act, Assert pattern shows that each test checks one behaviour.
To fake dependencies, you use libraries such as Moq or NSubstitute. That said, balance matters. A test that mocks everything only checks its own setup. For the data layer, many teams now prefer integration tests against a real database through Testcontainers instead of an in memory provider.
In addition, ASP.NET Core offers WebApplicationFactory, which starts your app inside a test. You can then check an endpoint at the HTTP level, including the middleware pipeline. Telling unit, integration and end to end tests apart is a clear sign of maturity.
What do interviewers ask about generic constraints and variance?
The question often opens with "what does where T : class do?" Constraints tell the compiler what you can do with a generic type. For example, where T : new() lets you create a new T inside the method. Likewise, where T : IComparable<T> lets you compare values.
Next come covariance and contravariance. IEnumerable<out T> is covariant, so you can pass an IEnumerable<string> where an IEnumerable<object> is expected. Action<in T> is contravariant and works the other way round.
So why can a List<string> not stand in for a List<object>? Because List both reads and writes. If the conversion worked, you could add an int to a list of strings. A candidate who explains that reasoning proves a real grasp of type safety.
How do delegates, events and lambdas relate?
A delegate is a type safe reference that carries a method like a variable. Func and Action are ready made generic delegates. A lambda expression, in other words, is short syntax for creating a delegate instance.
An event is an access restriction built on a delegate. Outside code can only subscribe with += or unsubscribe with -=. Meanwhile, only the declaring class can raise the event. This stops another class from wiping out every subscriber.
The usual follow up is memory leaks. If a short lived object subscribes to a long lived publisher and never unsubscribes, the GC cannot collect it. Put simply, an event subscription is also a resource that needs cleanup.
How do you choose between record, struct and class?
Records arrived in C# 9 and bring value equality. Two records with the same values are equal, whereas a class compares references by default. The with expression creates a copy with a small change, which makes immutable data models easy.
The practical split looks like this. Use a record for DTOs and event messages. Then use a struct, or a record struct, for small short lived values. Use a class for entities with identity and changing state. Copying a large struct often hurts performance, so keep structs small.
From here, the interviewer may move to pattern matching. Switch expressions and property patterns, combined with records, let you write business rules that read clearly.
Which live coding tasks should you expect?
Among C Sharp interview questions, live coding is usually of medium difficulty. The goal is not an algorithm contest. Instead, the interviewer wants clean, testable code. These are the tasks I see most:
- Count word frequency in a text with a Dictionary or GroupBy.
- Merge two sorted lists in a single pass.
- Build a simple LRU cache with a Dictionary and a LinkedList.
- Fetch data from an API asynchronously and handle errors and timeouts.
Before you code, ask about edge cases: empty input, null and very large data. Then write a simple solution and discuss complexity afterwards. This order shows the interviewer what working with you will feel like.
How do questions change for senior .NET roles?
As seniority rises, the questions shift to system design. Take an open question like "how would you build an order service that handles thousands of requests per second?" Caching, queues, database indexes and observability all come up together.
At this stage, explaining trade offs matters as much as technical accuracy. Microservices or a modular monolith has no single right answer. Instead, team size and release frequency drive the decision. I covered a similar debate on the front end in my article on micro frontend architecture.
You will also hear "how do you diagnose performance problems?" Concrete examples with dotnet-counters, dotnet-trace or memory dump analysis make a real difference.
Why does web performance and SEO knowledge help a .NET developer?
On business projects, the back end developer owns much of the page speed. If server response time is slow, the user waits no matter how good the front end is. So client facing teams prefer developers who understand the link between speed and search visibility.
For example, response compression, output caching and correct HTTP headers in ASP.NET Core bring measurable gains. I explain that link in how site speed affects SEO. For the measurement side, see my Lighthouse performance test guide.
Redirects and crawl rules also live on the back end. After a migration, you can check 301 chains with the redirect checker and draft crawl rules with the robots.txt generator. For the wider picture, read my technical SEO tips.
How should you prepare for C Sharp interview questions in the final week?
Spend the last week making what you know easy to explain, not learning new topics. In my experience the plan below works well. Treat it as a starting point, not a guarantee:
- Answer two questions from each section above out loud and record yourself.
- Solve one short live coding task a day with a timer running.
- For every project on your CV, prepare one hard bug and how you fixed it.
- Find out which .NET version and architecture the company uses from the job ad or its tech blog.
Finally, remember to ask questions yourself. Asking about code review, testing culture and release frequency shows real interest in the team. If you sit on the hiring side and need a technical partner for a web project, take a look at my web design service.




