Bobby Encoded
PostsAbout
PostsAbout

© 2026 Bobby Jose

← Back to Blog

C# Quick Reference Guide

February 17, 2025 · 7 min read

C#, .NET, Quick Reference, Fundamentals, Interview Prep

Introduction

This is the final post in the C# interview series - a quick reference for the questions that come up again and again. Each answer is concise but complete enough to satisfy an interviewer.


Language Fundamentals

What's the difference between const and readonly?

public class Constants
{
    public const int MaxItems = 100;        // Compile-time, inlined
    public readonly int MinItems;           // Runtime, set in constructor
    public static readonly int Default = ComputeDefault();  // Runtime, once

    public Constants(int min)
    {
        MinItems = min;  // Can assign in constructor
        // MaxItems = 50;  // Error! const can't be changed
    }
}

Gotcha: const values are baked into calling assemblies at compile time. If you change a const in a library and don't recompile consumers, they'll still use the old value.

Explain boxing and unboxing

int value = 42;
object boxed = value;     // Boxing: copy to heap, wrap in object
int unboxed = (int)boxed; // Unboxing: copy back to stack

// Performance cost in hot paths
var list = new ArrayList();
for (int i = 0; i < 1000000; i++)
    list.Add(i);  // Boxing every int!

// Fix: Use generic collections
var list = new List<int>();  // No boxing

What are delegates and events?

// Delegate: Type-safe function pointer
public delegate int Calculator(int a, int b);
Calculator add = (a, b) => a + b;
int result = add(5, 3);  // 8

// Built-in delegates
Func<int, int, int> multiply = (a, b) => a * b;  // Returns value
Action<string> log = msg => Console.WriteLine(msg);  // No return
Predicate<int> isEven = n => n % 2 == 0;  // Returns bool

// Event: Encapsulated delegate
public class Button
{
    public event EventHandler? Clicked;

    public void OnClick()
    {
        Clicked?.Invoke(this, EventArgs.Empty);
    }
}

// Gotcha: Event memory leaks
button.Clicked += HandleClick;  // If you don't -= you have a leak

Async and Threading

What's the difference between Task.Run and Task.Factory.StartNew?

// Task.Run - Simple, sane defaults
await Task.Run(() => DoWork());

// Task.Factory.StartNew - More control, dangerous defaults
await Task.Factory.StartNew(
    () => DoWork(),
    CancellationToken.None,
    TaskCreationOptions.DenyChildAttach,  // Important!
    TaskScheduler.Default);

// Gotcha: StartNew with async delegate
await Task.Factory.StartNew(async () => await DoWorkAsync());
// Returns Task<Task>! Not what you want.

// Fix: Unwrap or just use Task.Run
await Task.Run(() => DoWorkAsync());  // Handles this correctly

Explain CancellationToken

public async Task ProcessAsync(CancellationToken cancellationToken)
{
    foreach (var item in items)
    {
        // Check for cancellation
        cancellationToken.ThrowIfCancellationRequested();

        // Pass token to async operations
        await ProcessItemAsync(item, cancellationToken);
    }
}

// Creating tokens
using var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(30));  // Timeout

try
{
    await ProcessAsync(cts.Token);
}
catch (OperationCanceledException)
{
    // Handle cancellation
}

Memory and Performance

Explain IDisposable and using

public class ResourceHolder : IDisposable
{
    private bool _disposed;
    private readonly Stream _stream;

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (_disposed) return;

        if (disposing)
        {
            _stream?.Dispose();  // Dispose managed resources
        }

        // Free unmanaged resources here
        _disposed = true;
    }

    ~ResourceHolder()  // Finalizer - rarely needed
    {
        Dispose(false);
    }
}

// Modern using declaration (C# 8+)
using var file = File.OpenRead("test.txt");
// Disposed at end of scope

// IAsyncDisposable for async cleanup
await using var resource = new AsyncResource();

What is the Large Object Heap (LOH)?

Objects larger than 85KB (on 64-bit) are allocated on the LOH instead of the regular heap. The LOH:

  • Is not compacted (by default) - causes fragmentation
  • Collected only during Gen 2 collections
  • Can be configured: GCSettings.LargeObjectHeapCompactionMode

Tip: Avoid creating large temporary objects. Use ArrayPool<T> for buffers.

Explain Garbage Collection generations

  • Gen 0: Short-lived objects, collected frequently
  • Gen 1: Buffer between short and long-lived
  • Gen 2: Long-lived objects, collected infrequently

Objects that survive collection are promoted to the next generation. Most objects die young (Gen 0), so this strategy is efficient.


Collections and LINQ

When to use which collection?

CollectionBest ForAvoid When
List<T>Sequential access, index lookupFrequent insertions in middle
Dictionary<K,V>Key-based lookupOrdered iteration
HashSet<T>Unique items, contains checksNeed ordering
Queue<T>FIFO processingRandom access
Stack<T>LIFO processingRandom access
LinkedList<T>Frequent insert/removeIndex-based access

LINQ deferred vs immediate execution

// Deferred - query built, not executed
var query = users.Where(u => u.IsActive);  // No execution yet

// Immediate - executes now
var list = users.Where(u => u.IsActive).ToList();  // Executes
var count = users.Count(u => u.IsActive);  // Executes
var first = users.First(u => u.IsActive);  // Executes

// Gotcha: Multiple enumeration
var query = GetExpensiveQuery();
var count = query.Count();  // Executes
var list = query.ToList();  // Executes AGAIN

// Fix: Materialize once
var list = query.ToList();
var count = list.Count;  // Uses cached list

ASP.NET Core

Explain the middleware pipeline

app.UseExceptionHandler("/error");  // Runs last (wraps everything)
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();  // Must be before Authorization
app.UseAuthorization();
app.MapControllers();

// Custom middleware
app.Use(async (context, next) =>
{
    // Before
    await next();  // Call next middleware
    // After
});

Order matters! Authentication must come before Authorization, etc.

Explain dependency injection lifetimes

services.AddTransient<IService, Service>();   // New instance every injection
services.AddScoped<IService, Service>();      // One per request
services.AddSingleton<IService, Service>();   // One for app lifetime

Gotcha: Don't inject scoped services into singletons (captive dependency).

What's the difference between AddMvc, AddControllers, and AddControllersWithViews?

services.AddControllers();           // API only, no views
services.AddControllersWithViews();  // MVC with Razor views
services.AddRazorPages();            // Razor Pages only
services.AddMvc();                   // Everything (legacy)

Design and Architecture

Explain microservices vs monolith

MicroservicesMonolith
Independent deploymentSingle deployment
Technology diversityConsistent stack
Complex operationsSimpler operations
Network latencyIn-process calls
Scale independentlyScale entire app

Start with monolith, extract services when you have clear boundaries and team scale.

What is CQRS?

Command Query Responsibility Segregation: separate read and write models.

// Command - changes state
public record CreateOrderCommand(int CustomerId, List<OrderItem> Items);

// Query - reads state
public record GetOrderQuery(int OrderId);

// Separate handlers, potentially separate databases
public class CreateOrderHandler : ICommandHandler<CreateOrderCommand> { }
public class GetOrderHandler : IQueryHandler<GetOrderQuery, OrderDto> { }

Useful when read and write patterns differ significantly.

Explain Event Sourcing briefly

Instead of storing current state, store the sequence of events that led to it:

// Instead of: UPDATE Account SET Balance = 150
// Store events:
AccountCreated { InitialBalance = 0 }
MoneyDeposited { Amount = 200 }
MoneyWithdrawn { Amount = 50 }
// Current state derived by replaying events

Benefits: Complete audit trail, temporal queries, event-driven architecture. Costs: Complexity, eventual consistency, storage growth.


Quick Syntax Reference

Null handling

// Null-conditional
var name = user?.Profile?.Name;

// Null-coalescing
var name = user?.Name ?? "Anonymous";

// Null-coalescing assignment
list ??= new List<string>();

// Null-forgiving (careful!)
string name = GetPossiblyNull()!;  // Trust me, it's not null

Pattern matching summary

// Type pattern
if (obj is string s) { }

// Property pattern
if (user is { IsActive: true, Role: "Admin" }) { }

// Switch expression
var discount = customer switch
{
    { IsPremium: true } => 0.2m,
    { YearsActive: > 5 } => 0.1m,
    _ => 0m
};

// List pattern (C# 11)
if (arr is [1, 2, .. var rest]) { }

Series Conclusion

This series covered the C# and .NET topics that come up most in interviews. The key to doing well:

  1. Know the fundamentals cold - value/reference types, async/await, LINQ
  2. Understand the why - Don't just memorize, know when to use what
  3. Have real examples - "At my job, we had this issue with..."
  4. Know the gotchas - Interviewers love edge cases
  5. Stay current - Records, pattern matching, .NET 8 features

Good luck with your interviews!


Sources and Further Reading

For continued learning, I recommend:

  • Real .NET Interview Questions from 2024/2025
  • Top .NET Interview Questions - DEV Community
  • Advanced C# Interview Questions - Turing
  • .NET Interview Questions - InterviewBit

Part 7 of the C# Interview Prep series.

← Previous

Choosing a React Framework in 2025: A Solo Developer's Hard-Earned Lessons

Next →

Testing in .NET: Unit Tests, Mocking, and Integration Testing