Interview Cracker GitHub

C# Interview Questions


🟢 Q. Value types vs reference types — and where does each one live?

Answer. Value types (int, struct, enum) are stored in place — on the stack when local, inline inside the object that contains them — and are copied wholesale on assignment. Reference types (class, string, delegate, arrays) store a reference; the object lives on the managed heap and assignment copies the reference, not the object.

The follow-ups that separate a definition from understanding:

See examples/csharp/basics/value_vs_reference_types.cs.


🟢 Q. == vs .Equals() vs ReferenceEquals — when do they disagree?

Answer. ReferenceEquals(a, b) (and object.ReferenceEquals) asks "same object on the heap?" a == b is an operator: for most reference types it calls Equals, but string and types that overload == decide otherwise. a.Equals(b) is the virtual value-equality method.

The disagreements that actually bite:


🟡 Q. async/await — what does await actually do, and what is the

async void trap?

Answer. await does not block a thread. It registers the rest of the method as a continuation to run when the awaited task completes, and returns from the method now (the method becomes a state machine). The thread goes back to the pool; the continuation runs later, on the captured synchronization context (in UI code, back on the UI thread).

The traps:


🟡 Q. interface vs abstract class — and what did C# 8 default

interface members change?

Answer. An interface is a capability contract: no state, and (before C# 8) no implementation. A class can implement any number of them. An abstract class is a common base: it can carry state and shared behaviour, but a class has exactly one base class, so it competes with your real inheritance chain. The decision: repeated state/behaviour you want written once → abstract base; a capability that unrelated types should share → interface.

What C# 8 changed: interfaces can now have default implementations. That softens the line — an interface can carry behaviour — but it does not remove the distinction, because a default interface method still cannot touch instance state (there is none) and, more importantly, the reason to prefer an interface is the multiple-implementation freedom an abstract base cannot give. Default members are for extending an existing interface without breaking implementers (add a method with a sensible default), not for replacing a base class.

See examples/csharp/oop/interface_vs_abstract_class.cs.


🔴 Q. What is the difference between Task, ValueTask, and

Task<T> — and when is each the right return type?

Answer. Task<T> is the standard async result: a heap-allocated object representing a computation that yields T. ValueTask<T> is the low-allocation alternative for methods that usually complete synchronously: it can either wrap an already-available T (no allocation) or a Task<T> (allocation happens only on the async path). Task (no T) is the fire-and-complete-notify version for work with no result.

The trade-off, stated precisely:

See examples/csharp/async/ for the Task.WhenAll and cancellation patterns.