C# Interview Questions
Interviews for C# roles tend to probe a consistent set of themes: the difference between value and reference semantics, the mechanics of async/await, the distinction between interfaces and abstract classes, memory management, and LINQ's deferred execution model. Rather than memorizing definitions, focus on being able to explain *why* the language behaves the way it does — interviewers usually follow up a correct answer with 'why does that matter in practice?' This topic works through the questions that come up most often, framed as an interviewer would ask them, with answers that go one level deeper than the one-line definition.
Cricket analogy: A commentator explaining a batsman's technique doesn't just say 'he plays straight' — he explains why keeping the elbow high controls the ball, the same way a strong interview answer explains why value types copy, not just that they do.
'What's the difference between value types and reference types?'
Value types (struct, including all numeric primitives, bool, and enum) store their data directly wherever the variable lives — on the stack for locals, or inline within the containing object for fields. Assigning one value-type variable to another copies the entire value. Reference types (class, interface, delegate, array, string) store a reference on the stack (or containing object) that points to data allocated on the managed heap; assignment copies the reference, so both variables end up pointing at the same object. This is why mutating a struct copy doesn't affect the original, but mutating a class instance through a second reference is visible through the first.
Cricket analogy: Handing a physical scorecard to a teammate gives them their own copy to scribble on, like a value type, while sharing a single scoreboard app link means everyone edits the same shared data, like a reference type.
struct Point { public int X, Y; }
class Box { public int Value; }
var p1 = new Point { X = 1, Y = 2 };
var p2 = p1; // full copy
p2.X = 99;
Console.WriteLine(p1.X); // 1 — unaffected
var b1 = new Box { Value = 1 };
var b2 = b1; // copies the reference, not the object
b2.Value = 99;
Console.WriteLine(b1.Value); // 99 — same object'Explain how async/await actually works under the hood'
The compiler transforms an async method into a state machine. When execution hits an await on an incomplete Task, the method returns control to its caller immediately (an incomplete Task representing the ongoing work), and the remainder of the method is registered as a continuation on that awaited task. When the awaited operation completes, the continuation resumes — by default, on a captured SynchronizationContext if one exists (e.g. UI thread), otherwise on a thread pool thread. Crucially, await does not block a thread while waiting; it frees the current thread to do other work, which is why async is so effective for I/O-bound scalability.
Cricket analogy: When a match is interrupted by rain, the scorer doesn't discard the innings; the exact state of runs, overs, and wickets is preserved and play resumes from that precise point once conditions clear, whether on the same ground staff or a substitute crew.
A frequent follow-up: 'why does .Result or .Wait() on an async method sometimes deadlock in UI or ASP.NET (classic) apps?' Because blocking synchronously waits for the task, while the task's continuation is waiting to resume on the same captured context that's now blocked — a classic deadlock. ConfigureAwait(false) in library code avoids capturing that context.
'When would you choose an interface over an abstract class?'
Choose an interface to define a capability contract that unrelated types can implement — a class can implement any number of interfaces, so interfaces model 'can-do' relationships (IDisposable, IComparable). Choose an abstract class when you want to share actual implementation code and state across a family of closely related types, and when 'is-a' single inheritance is the natural relationship — a class can inherit from only one base class. Since C# 8, default interface methods blur the line slightly by letting interfaces carry implementation, but the single-inheritance vs. multiple-interface distinction remains the primary deciding factor in practice. A weak interview answer is 'interfaces have no implementation, abstract classes do' — this has been only partially true since default interface methods arrived in C# 8. A stronger answer leads with the inheritance-model distinction (single vs. multiple) rather than the implementation-presence distinction. Most LINQ query operators (Where, Select, OrderBy) don't execute when you write the query — they build up an expression describing the work and only execute when the sequence is actually enumerated, e.g. by a foreach, .ToList(), or .Count(). This means a query can observe changes to its underlying source if the source is mutated between query definition and enumeration, and it means the same query variable can be re-enumerated to get fresh results each time. Operators like ToList(), ToArray(), Count(), First(), and Sum() are exceptions — they force immediate execution.
Cricket analogy: A player who 'can bowl' might also 'can bat' and 'can captain' simultaneously, like implementing multiple interfaces, whereas being a specific 'fast bowler' inherits shared training from one lineage, like an abstract class; a scorecard query built before the innings starts only tallies runs once the innings is actually played out, like deferred LINQ execution.
- Value types copy their data on assignment; reference types copy a reference, so both variables alias the same object.
- async/await compiles into a state machine; await frees the thread instead of blocking it while waiting.
- Blocking on async code with .Result/.Wait() can deadlock when a SynchronizationContext is captured and blocked.
- Interfaces model 'can implement many' capability contracts; abstract classes model single-inheritance 'is-a' relationships with shared state.
- Default interface methods (C# 8+) let interfaces carry implementation, but the multiple vs. single inheritance distinction still drives the choice.
- Most LINQ operators use deferred execution — the query runs only when enumerated, not when defined.
Practice what you learned
1. What happens when you assign one struct variable to another?
2. What does the compiler generate for a method marked `async`?
3. Why can calling `.Result` on a Task deadlock in a UI application?
4. What is the primary structural reason to choose an abstract class over an interface?
5. What does 'deferred execution' mean for a LINQ query built with Where and Select?
Was this page helpful?
You May Also Like
async/await Explained
Understand how async and await let you write non-blocking, readable asynchronous code in C#, and how the compiler transforms it into a state machine.
Interfaces in C#
Understand interfaces as pure behavioral contracts that any type can implement, including default interface methods, explicit implementation, and multiple interface inheritance.
LINQ Basics
Language Integrated Query lets you write expressive, type-safe queries directly in C# over collections, databases, and XML using a unified syntax.
Common C# Pitfalls
A tour of the mistakes that trip up C# developers most often — from closures over loop variables to silent null reference bugs and misused async void.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics