Skip to content

PUBLIC QUESTION PREVIEW

C# & .NET interview questions

C# language, async/await, LINQ, ASP.NET Core, EF Core, Dapper, CLR internals — the full Microsoft stack.

Showing 12 of 110 source questions. Sample answers are study material, not employer-specific hiring guidance.

How to work through this preview

Attempt each question before reading its answer. For a concept, give a definition and a concrete example. For code, state constraints, walk through a small input, and check complexity. For design, separate requirements from assumptions and explain failure handling. Use real situations for behavioral answers.

QUESTION 1 / easy / conceptual

What is the difference between value types and reference types in C#?

Supplied choices

  1. Value types are stored on the heap; reference types on the stack
  2. Value types store data directly and are copied by value; reference types store a heap pointer and share the same object on assignment
  3. Reference types are always faster than value types
  4. Value types cannot be null under any circumstances

Source answer choice: B. Value types store data directly and are copied by value; reference types store a heap pointer and share the same object on assignment

Sample answer

Value types (int, bool, char, struct, enum): stored on the stack (or inline in objects). Assignment copies the value. They inherit from System.ValueType. Reference types (class, string, array, delegate): stored on the heap; variables hold a reference (pointer). Assignment copies the reference — both variables point to the same object. int a = 5; int b = a; b = 10; // a is still 5 — value was copied var p1 = new Person("Alice"); var p2 = p1; p2.Name = "Bob"; // p1.Name is now "Bob" — shared reference Key implications: - Passing a value type to a method = copy (changes don't affect the caller's variable) - Passing a reference type = shared reference (changes to object properties are visible to caller) - string is a reference type but behaves like a value type due to immutability

Hints to guide your next attempt

  • Where is each stored in memory?
  • What happens when you assign one to another?

Source tags: value-types / reference-types / memory / stack / heap / csharp

QUESTION 2 / medium / conceptual

How does async/await work under the hood in C#? What is a state machine?

Sample answer

When you write an async method, the C# compiler transforms it into a state machine — a class that tracks where execution left off at each await point. When await encounters an incomplete Task: 1. The current method returns control to the caller immediately (doesn't block the thread) 2. The continuation (code after await) is registered as a callback 3. When the awaited Task completes, the continuation resumes — possibly on the same thread (via SynchronizationContext) or a thread pool thread async/await is NOT threading — it's cooperative multitasking. One thread can handle many async operations by being freed during I/O waits. SynchronizationContext: in UI apps (WPF, WinForms), continuations resume on the UI thread. In ASP.NET Core, there's no sync context — continuations resume on any thread pool thread. ConfigureAwait(false): opt out of capturing the sync context. Use in library code to avoid deadlocks and context-switch overhead.

Hints to guide your next attempt

  • The compiler transforms async methods into a state machine
  • await does NOT block the thread — what does it do instead?

Source tags: async / await / state-machine / synchronization-context / csharp

QUESTION 3 / easy / conceptual

What is a delegate in C#? How does it differ from an event?

Supplied choices

  1. Delegates are only for methods returning void; events can return any type
  2. Events can be invoked from any class; delegates can only be invoked from within the declaring class
  3. Delegates define a method signature and are type-safe function pointers; events add restrictions preventing external invocation and direct assignment
  4. There is no difference — event is purely syntactic sugar with identical runtime behavior

Source answer choice: C. Delegates define a method signature and are type-safe function pointers; events add restrictions preventing external invocation and direct assignment

Sample answer

Delegate: a type-safe function pointer. It defines a method signature and you can store and invoke any method matching that signature. delegate int MathOp(int a, int b); MathOp add = (a, b) => a + b; int result = add(3, 4); // 7 Event: a field backed by a delegate, but with access restrictions enforced by the compiler: 1. External code can only += (subscribe) or -= (unsubscribe) — cannot = (replace) or invoke directly 2. Only the declaring class can invoke (raise) the event This makes events safe for the publisher-subscriber pattern — subscribers can't accidentally wipe out other subscribers or fire the event themselves. Func<T,TResult>, Action<T>, and Predicate<T> are built-in generic delegate types that eliminate the need to declare custom delegate types for most scenarios.

Hints to guide your next attempt

  • Delegate = type-safe function pointer
  • Event restricts what outside code can do with the delegate

Source tags: delegate / event / multicast / publisher-subscriber / csharp

QUESTION 4 / medium / conceptual

What is LINQ deferred execution? When does it cause bugs?

Sample answer

LINQ queries on IEnumerable<T> are lazy — the query definition is just a description of the computation. It doesn't execute until you iterate (foreach, .ToList(), .Count(), .First(), etc.). var query = numbers.Where(n => n > 5); // NOT executed yet numbers.Add(100); // modifies the source var result = query.ToList(); // executes NOW — includes 100 This is called deferred execution. Benefits: efficient chaining (one pass), works with infinite sequences. Common bugs: 1. Source modified between query and execution — results include/exclude unexpected elements 2. Multiple enumeration — calling .Count() then foreach executes the query TWICE (costly for DB queries) 3. Captured variables — if a closure captures a loop variable, all iterations see the last value Fix: call .ToList() or .ToArray() immediately to materialize a snapshot. IQueryable<T> (EF Core) is also deferred — the query is translated to SQL and runs when materialized. Never compose IQueryable<T> after the DbContext is disposed.

Hints to guide your next attempt

  • When does a LINQ query actually execute?
  • What if the source collection changes between definition and iteration?

Source tags: linq / deferred-execution / lazy-evaluation / ienumerable / csharp

QUESTION 5 / easy / conceptual

What are generics in C#? What are type constraints and when do you use them?

Sample answer

Generics allow writing type-safe, reusable code that works for multiple types without boxing or casting. public T Max<T>(T a, T b) where T : IComparable<T> => a.CompareTo(b) >= 0 ? a : b; Max(3, 7); // int Max("a", "z"); // string Type constraints (where T : ...): - class: T must be a reference type - struct: T must be a value type - new(): T must have a parameterless constructor - SomeBaseClass: T must be or derive from SomeBaseClass - ISomeInterface: T must implement the interface - notnull: T must be non-nullable (C# 8+) - unmanaged: T must be an unmanaged type (C# 7.3+) Without constraints, you can only call object-level methods on T. Each constraint you add unlocks more operations. Generics are resolved at compile time — value types avoid boxing (unlike non-generic collections like ArrayList).

Hints to guide your next attempt

  • Generics = compile-time type safety without boxing
  • Constraints open up operations on T

Source tags: generics / type-constraints / type-safety / csharp

QUESTION 6 / medium / conceptual

What is the difference between IEnumerable<T> and IQueryable<T>?

Supplied choices

  1. IQueryable is faster for in-memory collections; IEnumerable is for database queries
  2. Both execute queries in memory — the difference is just compile-time type checking
  3. IEnumerable executes LINQ in C# memory; IQueryable builds an expression tree executed server-side (e.g. as SQL)
  4. IQueryable is deprecated in .NET 6+

Source answer choice: C. IEnumerable executes LINQ in C# memory; IQueryable builds an expression tree executed server-side (e.g. as SQL)

Sample answer

IEnumerable<T>: LINQ operations are C# delegates executed in memory. All data is loaded first, then filtered in the application. IQueryable<T>: represents an unexecuted query. LINQ operations build an expression tree that a provider (e.g. EF Core) translates to SQL and runs on the database server. // IQueryable — translates to SQL: SELECT * FROM Users WHERE Age > 18 var adults = dbContext.Users.Where(u => u.Age > 18).ToList(); // IEnumerable — loads ALL users into memory, then filters in C# var adults = dbContext.Users.AsEnumerable().Where(u => u.Age > 18).ToList(); The AsEnumerable() version is catastrophic for large tables. Always compose on IQueryable<T> and only call .ToList() when you need the results. IQueryable<T> extends IEnumerable<T>, so all LINQ operators work on both — but where the computation happens differs entirely.

Hints to guide your next attempt

  • Where is the filtering done — in C# memory or on the database server?
  • Which one builds an expression tree?

Source tags: ienumerable / iqueryable / linq / ef-core / expression-tree / csharp

QUESTION 7 / medium / conceptual

What is boxing and unboxing in C#? Why does it matter for performance?

Supplied choices

  1. Boxing only affects string types and has minimal performance impact
  2. Boxing allocates a heap object wrapping the value type — adds GC pressure and memory copy overhead
  3. Boxing is handled by the JIT so it has zero runtime performance impact
  4. Boxing only occurs when using reflection APIs

Source answer choice: B. Boxing allocates a heap object wrapping the value type — adds GC pressure and memory copy overhead

Sample answer

Boxing: converting a value type (e.g. int) to a reference type (object or interface). Creates a new heap-allocated wrapper object containing a copy of the value. Unboxing: casting the boxed object back to the value type — involves a null check and type check then a copy. int x = 42; object boxed = x; // boxing — heap allocation + copy int y = (int)boxed; // unboxing — type check + copy Performance cost: 1. Heap allocation per box → increases GC pressure 2. Two memory copies (value → heap on box, heap → stack on unbox) Classic problem — pre-generic collections: ArrayList list = new ArrayList(); list.Add(42); // boxes int int n = (int)list[0]; // unboxes Fix: use generic collections (List<int>, Dictionary<int,string>) — no boxing for value types because the JIT generates type-specific code. Other boxing hotspots to watch: passing a struct to a method taking object, string.Format("{0}", structValue), implementing a non-generic interface on a struct.

Hints to guide your next attempt

  • What heap allocation happens when you box a value type?
  • Where did this commonly occur in pre-generics code?

Source tags: boxing / unboxing / performance / gc / value-types / csharp

QUESTION 8 / medium / coding

Implement the IDisposable pattern correctly in C#. What is the "using" statement?

Sample answer

public class DatabaseConnection : IDisposable { private SqlConnection _conn; private bool _disposed = false; public DatabaseConnection(string connStr) { _conn = new SqlConnection(connStr); _conn.Open(); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); // tell GC: finalizer not needed } protected virtual void Dispose(bool disposing) { if (!_disposed) { if (disposing) _conn?.Dispose(); // clean up managed resources // release unmanaged handles (file handles, native memory) here _disposed = true; } } ~DatabaseConnection() => Dispose(false); // safety net finalizer } // Usage — auto-disposes at end of scope: using var conn = new DatabaseConnection(connStr); // C# 8 declaration // or using (var conn = new DatabaseConnection(connStr)) { ... } The bool disposing param allows the finalizer (which runs on the GC thread) to skip managed resource cleanup (already GC'd) while still releasing unmanaged resources.

Hints to guide your next attempt

  • Dispose(bool disposing) separates managed vs unmanaged cleanup
  • GC.SuppressFinalize prevents double-cleanup

Source tags: idisposable / using / finalizer / resource-management / csharp

QUESTION 9 / easy / conceptual

What is the difference between struct and class in C#? When should you use a struct?

Supplied choices

  1. Structs support inheritance from other structs but not from classes
  2. Structs are always allocated on the heap just like classes
  3. Structs are value types — stack-allocated by default, copied by value, no inheritance; classes are reference types stored on the heap
  4. Classes are always faster than structs because they avoid copying

Source answer choice: C. Structs are value types — stack-allocated by default, copied by value, no inheritance; classes are reference types stored on the heap

Sample answer

struct (value type): stored on stack (or inline in containing object). Copied by value on assignment. No inheritance (can implement interfaces). Cannot be null without "?". No finalizer allowed. class (reference type): stored on heap, variable holds a reference. Assignment copies the reference. Supports inheritance. Can be null. Can have a finalizer. Use struct when: - Small, simple data (Point, Color, DateTime, Money) — typically ≤ 16 bytes rule of thumb - Frequently allocated/deallocated to reduce GC pressure - Semantically immutable (value equality makes sense) - Hot paths where stack allocation helps performance Avoid struct when: - Large data (copying is expensive for large structs) - Need polymorphism or inheritance - Frequently boxed (negates the benefit) - Mutable shared state C# modifiers for struct: - readonly struct: compiler-enforced immutability, allows in parameter optimizations - ref struct: stack-only allocation (Span<T>) — can't be boxed or stored on heap

Hints to guide your next attempt

  • Value type vs reference type
  • Stack vs heap, no inheritance, size matters

Source tags: struct / class / value-type / reference-type / performance / csharp

QUESTION 10 / easy / conceptual

What is the difference between const and readonly in C#?

Supplied choices

  1. const can be assigned in a constructor; readonly must be assigned at the declaration site
  2. Both are evaluated at compile time — they are interchangeable
  3. const is inlined at compile time and must be a primitive; readonly is a runtime constant settable in the constructor
  4. readonly is implicitly static; const is instance-level

Source answer choice: C. const is inlined at compile time and must be a primitive; readonly is a runtime constant settable in the constructor

Sample answer

const: compile-time constant. Evaluated by the compiler and inlined at every call site. Must be a primitive (int, string, bool, etc.). Implicitly static. Cannot be changed after compilation. readonly: runtime constant. Can be any type. Set only in the field initializer or constructor. Can differ per instance. public class Config { public const string AppName = "ResumeWorlds"; // compile-time, inlined public readonly DateTime LaunchDate; // runtime, per-instance public static readonly List<string> AllowedRoles = // complex type — must be readonly new() { "admin", "user" }; public Config() { LaunchDate = DateTime.UtcNow; } } Critical pitfall with const: if you publish a library with a public const and consumers reference it, the value is baked into their compiled assemblies. Changing the const value in a new version of your library requires consumers to recompile — they won't see the change just by updating the DLL. Prefer static readonly for public API constants that might ever change.

Hints to guide your next attempt

  • When is each evaluated — compile time or runtime?
  • Which can be set in a constructor?

Source tags: const / readonly / compile-time / immutability / csharp

QUESTION 11 / medium / conceptual

What are extension methods in C#? When and how do you write one?

Sample answer

Extension methods let you add new methods to existing types — including sealed classes, interfaces, and third-party types — without modifying or inheriting from them. Requirements: 1. Must be in a static class (usually named TypeExtensions) 2. Must be a static method 3. First parameter must have the this modifier public static class StringExtensions { public static bool IsNullOrEmpty(this string s) => string.IsNullOrEmpty(s); public static string Truncate(this string s, int maxLength) => s.Length <= maxLength ? s : s[..maxLength] + "…"; } // Called as if they're instance methods: "hello world".Truncate(5); // "hello…" "".IsNullOrEmpty(); // true All LINQ operators (Where, Select, OrderBy…) are extension methods on IEnumerable<T>. Use for: utility methods on types you don't own, fluent builder APIs, domain helpers. Avoid: adding methods that really belong in the type itself, creating confusion about where methods are defined.

Hints to guide your next attempt

  • Static class, static method, first parameter with "this" keyword
  • Adds methods to types you don't own

Source tags: extension-methods / static / linq / csharp

QUESTION 12 / medium / conceptual

What is the difference between Func<T>, Action<T>, and Predicate<T> in C#?

Sample answer

All three are built-in generic delegate types — you rarely need to define custom delegates. Action<T>: takes parameters, returns void. Action<string> log = msg => Console.WriteLine(msg); Action<int, int> print = (a, b) => Console.WriteLine(a + b); Func<T, TResult>: takes parameters, returns a value. Last type parameter is always the return type. Func<int, int, int> add = (a, b) => a + b; Func<string, bool> isLong = s => s.Length > 10; Predicate<T>: exactly Func<T, bool> — tests a condition. Used in List<T>.Find(), FindAll(), RemoveAll(). Predicate<int> isEven = n => n % 2 == 0; list.FindAll(isEven); Func supports up to 16 input type parameters + 1 return type. Action supports up to 16 input type parameters, void return. In practice: - Callbacks with no return value → Action<T> - Transforms, selectors, LINQ projections → Func<TIn, TOut> - Conditions / filters → Func<T, bool> (or Predicate<T> for List<T> methods) - Event handlers → EventHandler<TEventArgs>

Hints to guide your next attempt

  • Focus on return types
  • Predicate<T> is a specialized Func<T,bool>

Source tags: func / action / predicate / delegates / higher-order-functions / csharp

Check understanding, not recognition

  • Can you explain the answer without reading it?
  • Can you show when the approach fails or needs a different assumption?
  • Can you connect the concept to a real project, task, or decision you have made?
  • Can you distinguish a simplified interview example from a production requirement?

A LITTLE MORE CLARITY

Frequently asked questions

How many C# & .NET questions are shown here?

This public preview shows 12 questions with the supplied answers from the app's 110-question C# & .NET bank. Full interactive practice requires signing in.

Should I memorize the sample answers?

Use them as reference explanations, not a script or a guarantee of technical correctness for every environment. Explain your assumptions, validate code against your language and version, and discuss trade-offs. Behavioral answers should be built from your own experience.

What do hints and solution steps mean?

Hints identify ideas to consider before reading the answer. When the source question includes steps, they are shown as a separate walkthrough. Example capacities and design targets in a solution are assumptions for that exercise, not real system statistics.

BUILD YOUR NEXT OPPORTUNITY

Practice C# & .NET actively.

Sign in to use the full topic bank and interactive practice workspace.

Open Topic Practice