Advanced Design Patterns in C#: Code Examples

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Advanced Design Patterns in C#: From Theory to Practice



Design Patterns are proven solutions to typical software architecture problems. At the beginner level, developers get acquainted with Singleton, Factory, or Adapter. However, to create flexible, maintainable, and scalable systems on the .NET platform, mastery of more complex behavioral and structural patterns is necessary. In this article, we will cover three advanced patterns: Chain of Responsibility, State, and Visitor. Each pattern will be illustrated with a practical example in C# using modern language features (records, pattern matching, generics).



1. Chain of Responsibility



The Chain of Responsibility pattern allows passing a request sequentially along a chain of handlers until one of them processes the request. This eliminates the rigid coupling between the sender and the receiver. In C#, it is often used for middleware in ASP.NET Core or for data validation.



Example: access rights verification system

Suppose we have a request to perform an operation. Each handler checks one condition (authentication, authorization, limits).



// Base handler classpublic abstract class Handler{    protected Handler? NextHandler;

public void SetNext(Handler handler) => NextHandler = handler;

public abstract void Handle(RequestContext context);}

public class AuthenticationHandler : Handler{ public override void Handle(RequestContext context) { if (context.IsAuthenticated) { Console.WriteLine("Authentication passed."); NextHandler?.Handle(context); } else { Console.WriteLine("Error: user not authenticated."); } }}

public class AuthorizationHandler : Handler{ public override void Handle(RequestContext context) { if (context.Role == "Admin") { Console.WriteLine("Authorization passed."); NextHandler?.Handle(context); } else { Console.WriteLine("Error: insufficient permissions."); } }}

// Request contextpublic record RequestContext(bool IsAuthenticated, string Role);

// Usagevar authHandler = new AuthenticationHandler();var authzHandler = new AuthorizationHandler();authHandler.SetNext(authzHandler);

var context = new RequestContext(true, "Admin");authHandler.Handle(context);// Output:// Authentication passed.// Authorization passed.


This pattern is ideal for creating processing pipelines where each step can interrupt execution or pass control further.



2. State



The State pattern allows an object to change its behavior when its internal state changes. Externally, it looks as if the object changes its class. In C#, it is convenient to implement using interfaces and composition.



Example: order management in an online store

An order can be in the following states: New, Paid, Shipped, Delivered. Each state defines which actions are allowed.



// State interfacepublic interface IOrderState{    void Pay(Order order);    void Ship(Order order);    void Deliver(Order order);}

// Concrete statespublic class NewOrderState : IOrderState{ public void Pay(Order order) { Console.WriteLine("Order paid."); order.State = new PaidOrderState(); }

public void Ship(Order order) => Console.WriteLine("Error: order not yet paid."); public void Deliver(Order order) => Console.WriteLine("Error: order not yet shipped.");}

public class PaidOrderState : IOrde

Blogs

Book Recommendations