Design Patterns in C#

11 questions found

How do you implement a thread-safe Singleton pattern in modern C# using Lazy<T>?

Beginner
Lazy<T> defers creation of the instance until it's first accessed AND guarantees thread-safe initialization by default, making it the cleanest modern way to implement a Singleton without manually writing double-checked locking.
public class ConfigManager {
  private static readonly Lazy<ConfigManager> _instance = new(() => new ConfigManager());
  public static ConfigManager Instance => _instance.Value;
  private ConfigManager() { }
}
Real-world example Ensuring a single, shared application configuration or logging instance across the entire application.

Common follow-ups: Why is a plain 'static readonly' field alone not always sufficient to guarantee thread-safe lazy initialization?

Multithreading & Task Parallel Library

How does the Factory Method pattern work in C#, and what problem does it solve?

Beginner
A Factory Method encapsulates object-creation logic behind a method (often on a base class or interface) that subclasses or implementations override to decide WHICH concrete type to instantiate — this decouples the calling code from needing to know or reference specific concrete classes directly.
public abstract class ShapeFactory {
  public abstract IShape CreateShape();
}
public class CircleFactory : ShapeFactory {
  public override IShape CreateShape() => new Circle();
}
Real-world example Building a document-processing system where different factories create different parser implementations depending on file type.

Common follow-ups: How does the Factory Method pattern differ from the more general Abstract Factory pattern?

Interfaces & Abstract Classes

How would you implement the Strategy pattern in C# using either interfaces or delegates?

Intermediate
Define a common interface (or a delegate type) representing an interchangeable algorithm, implement multiple concrete strategies, and inject/pass the chosen one into the context class — modern C# often prefers a simple Func<> delegate over a full interface hierarchy for lightweight strategies.
// Interface-based
public interface IDiscountStrategy { decimal Apply(decimal price); }
public class PercentageDiscount : IDiscountStrategy {
  public decimal Apply(decimal price) => price * 0.9m;
}

// Delegate-based (lighter weight)
Func<decimal, decimal> percentageDiscount = price => price * 0.9m;
Real-world example Supporting multiple interchangeable pricing, sorting, or validation strategies that can be swapped at runtime.

Common follow-ups: When would you prefer the interface-based approach over the simpler delegate-based one for the Strategy pattern?

Delegates Events & Lambdas

How does the Repository pattern abstract data access, and what interface typically defines its contract?

Intermediate
The Repository pattern defines an interface (like IRepository<T>) exposing domain-focused CRUD operations, hiding the underlying data access technology (Entity Framework, raw SQL, an external API) behind that abstraction — letting business logic depend on the abstraction rather than a specific persistence mechanism.
public interface IRepository<T> {
  Task<T?> GetByIdAsync(int id);
  Task<IEnumerable<T>> GetAllAsync();
  Task AddAsync(T entity);
}
public class UserRepository : IRepository<User> {
  // implementation using EF Core, backed by AppDbContext
}
Real-world example Decoupling business logic from Entity Framework specifics, making it possible to swap the data layer or mock it easily in tests.

Common follow-ups: Is the Repository pattern still considered necessary on top of Entity Framework Core's DbSet<T>, which already provides similar abstraction?

Dependency Injection & IoC Principles

How would you implement the Decorator pattern in C# to add behavior to an object without modifying its original class?

Intermediate
Create a decorator class that implements the SAME interface as the object it wraps, holds a reference to the wrapped instance, and adds behavior before/after delegating the actual work to it — decorators can be chained/stacked to layer multiple behaviors independently.
public interface INotifier { void Send(string message); }
public class EmailNotifier : INotifier {
  public void Send(string message) => Console.WriteLine($"Email: {message}");
}
public class LoggingNotifierDecorator : INotifier {
  private readonly INotifier _inner;
  public LoggingNotifierDecorator(INotifier inner) { _inner = inner; }
  public void Send(string message) {
    Console.WriteLine($"Logging: about to send '{message}'");
    _inner.Send(message);
  }
}
Real-world example Wrapping a core notification service with logging, retry logic, or caching behavior without modifying its original implementation.

Common follow-ups: How does the Decorator pattern differ from simply using inheritance to add the extra behavior?

Interfaces & Abstract Classes

How would you implement the Observer pattern in C# using events, versus using the IObservable<T>/IObserver<T> interfaces from System?

Advanced
A simple event-based Observer uses standard C# events (publisher raises an event, subscribers attach handlers) — straightforward but tightly coupled to the delegate/event model. IObservable<T>/IObserver<T> (the Reactive Extensions foundation) formalizes the pattern with an explicit Subscribe()/OnNext()/OnCompleted()/OnError() contract, better suited for composable, LINQ-style event stream processing.
// Event-based
public class StockTicker { public event Action<decimal>? PriceChanged; }

// IObservable-based (more composable, works with Rx operators)
public class StockTickerObservable : IObservable<decimal> {
  public IDisposable Subscribe(IObserver<decimal> observer) { /* ... */ return Disposable.Empty; }
}
Real-world example Choosing plain events for a simple UI click handler versus IObservable<T> for a composable, filterable stream of live stock price updates.

Common follow-ups: What extra composability (like filtering or throttling) does Reactive Extensions (Rx.NET) add on top of raw IObservable<T>?

Delegates Events & Lambdas

How would you implement the Unit of Work pattern alongside the Repository pattern to coordinate multiple repository changes within a single transaction?

Advanced
Unit of Work tracks all changes made across multiple repositories during a business operation and commits them together in ONE atomic transaction (typically via SaveChanges() in EF Core) — this ensures related changes across different repositories either ALL succeed or ALL fail together, maintaining data consistency.
public interface IUnitOfWork : IDisposable {
  IRepository<Order> Orders { get; }
  IRepository<Customer> Customers { get; }
  Task<int> SaveChangesAsync();
}

// Usage
unitOfWork.Orders.Add(newOrder);
unitOfWork.Customers.Update(customer);
await unitOfWork.SaveChangesAsync(); // both changes committed together, atomically
Real-world example Ensuring an order placement (which updates both an Orders table AND a Customer's loyalty points) either fully succeeds or fully rolls back together.

Common follow-ups: Does Entity Framework Core's DbContext already implement the Unit of Work pattern internally, making a separate abstraction sometimes redundant?

Dependency Injection & IoC Principles

How would you implement the CQRS (Command Query Responsibility Segregation) pattern's basic structure in a C# application using MediatR-style handlers?

Advanced
CQRS separates WRITE operations (Commands, which change state and typically return little/nothing) from READ operations (Queries, which return data without side effects) into distinct request/handler pairs — often implemented with a mediator pattern where each Command/Query has its own dedicated Handler class, keeping business logic focused and independently testable.
public record CreateOrderCommand(int CustomerId, List<OrderItem> Items) : IRequest<int>;

public class CreateOrderHandler : IRequestHandler<CreateOrderCommand, int> {
  public async Task<int> Handle(CreateOrderCommand command, CancellationToken ct) {
    var order = new Order(command.CustomerId, command.Items);
    await _repository.AddAsync(order);
    return order.Id;
  }
}
Real-world example Structuring a complex domain's write and read operations into clearly separated, independently-scalable handler classes.

Common follow-ups: What benefits does CQRS provide specifically in systems with very different read versus write scaling requirements?

Records & Pattern Matching

How would you implement the Builder pattern using method chaining (a fluent API) to construct a complex object step by step?

Advanced
Define a builder class with methods that each set part of the object being built and RETURN 'this' (the builder itself), enabling method chaining; a final Build() method assembles and returns the fully-configured object — useful when an object has many optional configuration parameters that would otherwise require an unwieldy constructor with many arguments.
public class EmailBuilder {
  private readonly Email _email = new();
  public EmailBuilder To(string address) { _email.To = address; return this; }
  public EmailBuilder Subject(string subject) { _email.Subject = subject; return this; }
  public Email Build() => _email;
}

var email = new EmailBuilder().To("sam@x.com").Subject("Hello").Build();
Real-world example Constructing a complex configuration object (like an HTTP request or an email message) with many optional parameters readably.

Common follow-ups: How do C# 12's collection expressions or object initializer syntax sometimes reduce the NEED for a full Builder pattern for simpler cases?

Records & Pattern Matching

How would you implement the Chain of Responsibility pattern in C# to process a request through a series of independent handlers?

Advanced
Each handler holds a reference to the NEXT handler in the chain; it either processes the request itself and stops, or passes it along to the next handler if it can't handle it — commonly implemented in ASP.NET Core's own middleware pipeline, which is essentially a real-world Chain of Responsibility implementation.
public abstract class ValidationHandler {
  protected ValidationHandler? Next;
  public ValidationHandler SetNext(ValidationHandler next) { Next = next; return next; }
  public abstract bool Handle(Order order);
}
public class CreditCheckHandler : ValidationHandler {
  public override bool Handle(Order order) =>
    CheckCredit(order) && (Next?.Handle(order) ?? true);
}
Real-world example Building a multi-step validation pipeline for an order (credit check, inventory check, fraud check) where each step can short-circuit the chain.

Common follow-ups: How does ASP.NET Core's own middleware pipeline (app.Use(...)) directly mirror this exact pattern?

Interfaces & Abstract Classes

Showing 1–10 of 11