Skip to content

C# Interview Questions

Q: What is the difference between a class and a struct?

ClassStruct
TypeReference typeValue type
MemoryHeapStack (usually)
InheritanceYesNo (can implement interfaces)
DefaultnullZero-initialized
Use caseComplex objectsSmall, immutable data
class Person { public string Name { get; set; } }
struct Point { public int X; public int Y; }

Q: What is the difference between abstract class and interface?

Abstract ClassInterface
Multiple inheritanceNoYes
FieldsYesNo (only properties)
ConstructorsYesNo
Access modifiersYesPublic by default
ImplementationCan haveDefault methods (C# 8+)

Use abstract class when sharing implementation. Use interface for contracts.


Q: What is boxing and unboxing?

int value = 42;
object boxed = value; // boxing — value type → reference type (heap allocation)
int unboxed = (int)boxed; // unboxing — reference type → value type
// Avoid in hot paths — causes GC pressure

Q: What is the difference between == and .Equals()?

string a = new string("hello");
string b = new string("hello");
Console.WriteLine(a == b); // True — string overloads ==
Console.WriteLine(a.Equals(b)); // True
object x = 42;
object y = 42;
Console.WriteLine(x == y); // True — value comparison for boxed ints
Console.WriteLine(ReferenceEquals(x, y)); // False — different objects

Q: Explain virtual, override, and new keywords.

class Animal {
public virtual string Speak() => "...";
}
class Dog : Animal {
public override string Speak() => "Woof"; // polymorphic override
}
class Cat : Animal {
public new string Speak() => "Meow"; // hides base method, not polymorphic
}
Animal a = new Dog();
Console.WriteLine(a.Speak()); // "Woof" — override is polymorphic
Animal b = new Cat();
Console.WriteLine(b.Speak()); // "..." — new hides, not overrides

Q: What is the difference between readonly and const?

class Config {
public const double Pi = 3.14159; // compile-time, static, inlined
public readonly DateTime StartTime; // runtime, set in constructor only
public Config() {
StartTime = DateTime.Now;
}
}

Q: What is async/await and how does it work?

async/await is syntactic sugar over the Task-based Asynchronous Pattern (TAP). The compiler transforms the method into a state machine.

public async Task<string> FetchDataAsync(string url) {
using var client = new HttpClient();
string result = await client.GetStringAsync(url); // non-blocking
return result;
}
  • await suspends the method without blocking the thread
  • The continuation runs when the awaited task completes
  • Always use ConfigureAwait(false) in library code

Q: What is the difference between Task and ValueTask?

  • Task: always allocates on the heap
  • ValueTask: avoids allocation when the result is already available (synchronous path)
// Use ValueTask when the method often completes synchronously
public ValueTask<int> GetCachedValueAsync() {
if (_cache.TryGetValue("key", out int val))
return new ValueTask<int>(val); // no allocation
return new ValueTask<int>(FetchAsync());
}

Q: What is deadlock and how do you avoid it in async code?

Deadlock occurs when .Result or .Wait() is called on a Task in a context that has a synchronization context (e.g., ASP.NET classic, WinForms):

// DEADLOCK — don't do this
var result = GetDataAsync().Result;
// Safe options:
// 1. Use await all the way up
var result = await GetDataAsync();
// 2. Use ConfigureAwait(false) in library code
var result = await GetDataAsync().ConfigureAwait(false);

Q: What is the difference between IEnumerable and IQueryable?

IEnumerable<T>IQueryable<T>
ExecutionIn-memory (LINQ to Objects)Translated to query (LINQ to SQL/EF)
Where filteringClient-sideServer-side (SQL WHERE)
Use caseCollections, listsDatabases via EF Core
// IQueryable — filter happens in SQL
var users = dbContext.Users.Where(u => u.Age > 18).ToList();
// IEnumerable — all users loaded, then filtered in memory
IEnumerable<User> users = dbContext.Users.ToList();
var filtered = users.Where(u => u.Age > 18);

Q: What is deferred execution in LINQ?

LINQ queries are not executed when defined — they execute when iterated:

var query = numbers.Where(n => n > 5); // not executed yet
// ...
foreach (var n in query) { } // executed here
// Force immediate execution
var list = numbers.Where(n => n > 5).ToList();

Q: What is the difference between Span<T> and Memory<T>?

  • Span<T>: stack-only, zero-allocation slice of contiguous memory
  • Memory<T>: heap-safe version, can be used in async methods
string text = "Hello, World!";
ReadOnlySpan<char> span = text.AsSpan(0, 5); // "Hello" — no allocation
Console.WriteLine(span.ToString());

Q: What is IDisposable and when should you implement it?

Implement IDisposable when your class holds unmanaged resources (file handles, DB connections, etc.):

public class FileProcessor : IDisposable {
private FileStream _stream;
private bool _disposed;
public FileProcessor(string path) {
_stream = File.OpenRead(path);
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing) {
if (!_disposed) {
if (disposing) _stream?.Dispose();
_disposed = true;
}
}
}

Q: What are records and when should you use them?

// Record — immutable reference type with value-based equality
public record Person(string Name, int Age);
var p1 = new Person("Alice", 30);
var p2 = new Person("Alice", 30);
Console.WriteLine(p1 == p2); // True — value equality
// Non-destructive mutation
var p3 = p1 with { Age = 31 };

Use records for DTOs, value objects, and immutable data.


Q: What are primary constructors (C# 12)?

public class Service(ILogger logger, IRepository repo) {
public void DoWork() {
logger.Log("Working...");
repo.Save();
}
}

Q: What is pattern matching?

object obj = "Hello";
// Type pattern
if (obj is string s) Console.WriteLine(s.Length);
// Switch expression
string result = obj switch {
int n when n > 0 => "positive",
string s when s.Length > 5 => "long string",
null => "null",
_ => "other"
};
// Property pattern
if (person is { Age: > 18, Name: "Alice" }) { }

Q: What is the difference between string and StringBuilder?

// string — immutable, each concat creates a new object
string s = "";
for (int i = 0; i < 1000; i++) s += i; // 1000 allocations — bad
// StringBuilder — mutable buffer, single allocation
var sb = new StringBuilder();
for (int i = 0; i < 1000; i++) sb.Append(i);
string result = sb.ToString();

Q: What is dependency injection and how does it work in .NET?

// Register services
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddSingleton<ICache, MemoryCache>();
builder.Services.AddTransient<IEmailSender, SmtpEmailSender>();
// Inject via constructor
public class UserController(IUserService userService) {
public IActionResult Get(int id) => Ok(userService.GetById(id));
}

Lifetimes:

  • Singleton: one instance for the app lifetime
  • Scoped: one per request
  • Transient: new instance every time