Visitor Pattern in C#

The Visitor Pattern lets you add new behaviour to existing object structures without modifying their classes. Instead of changing the objects, you create a separate visitor that performs operations on them — preserving open/closed principle.

Real-Life Analogy: Visitors at a Company Office

Imagine a company office. On any day, it receives different types of visitors: a job applicant heading to HR, a tax auditor visiting Finance, a vendor talking to Procurement, or a journalist meeting PR.

Each visitor interacts differently with the department they visit. And each department knows how to respond to that visitor — without changing what it does day to day. The department is the element. The visitor brings the behaviour. This is exactly how the Visitor Pattern works in code.

Visitors entering company departments like HR, Finance, or PR
Like a company receiving different visitors — each interacting with departments in their own way — the Visitor Pattern lets you add logic to objects from the outside.

Benefits of Visitor Pattern

The Visitor Pattern makes it easy to add operations to complex object structures without changing their code. This is especially useful when working with many types that need to support different operations without becoming bloated.

  • Separates logic from the object model
  • Adds behaviour without modifying existing classes
  • Supports adding new operations without changing existing structures

When Should You Use It?

Use the Visitor Pattern when you have a stable class hierarchy and need to add multiple unrelated behaviours to it. It’s great for processing tree-like or composite structures where each element needs to be handled differently.

It's a good fit when:

  • You want to avoid polluting classes with many unrelated methods
  • You need to perform many distinct and unrelated operations across objects
  • The object structure is fixed but operations vary or grow over time

When Not to Use It: Avoid this pattern if your object structure changes frequently — because every time you add a new class, you must update all visitors.

What to Implement

To implement the Visitor Pattern, you need:

  • Visitor Interface: Declares visit methods for each element type
  • Concrete Visitor: Implements operations for each type
  • Element Interface: Declares an Accept() method
  • Concrete Elements: Implement the Accept() method and call back the visitor

This structure allows you to keep object classes clean while enabling flexible operations from outside.

How It Works in C#

Let's model a company with departments. Each department implements a shared interface and exposes an Accept() method. Visitors (like a job seeker or tax auditor) arrive and perform specific logic when interacting with each department.


// Visitor Interface
public interface IVisitor
{
    void Visit(HRDepartment hr);
    void Visit(FinanceDepartment finance);
}

// Element Interface
public interface IDepartment
{
    void Accept(IVisitor visitor);
}

// Concrete Elements
public class HRDepartment : IDepartment
{
    public void Accept(IVisitor visitor) => visitor.Visit(this);
    public string OpenPositions => "Software Engineer, UI Designer";
}

public class FinanceDepartment : IDepartment
{
    public void Accept(IVisitor visitor) => visitor.Visit(this);
    public decimal Budget => 2500000;
}

// Concrete Visitor
public class VisitorFromOutside : IVisitor
{
    public void Visit(HRDepartment hr)
    {
        Console.WriteLine($"Checking open positions in HR: {hr.OpenPositions}");
    }

    public void Visit(FinanceDepartment finance)
    {
        Console.WriteLine($"Reviewing financials: budget is ${finance.Budget}");
    }
}

Client usage:


var departments = new List<IDepartment>
{
    new HRDepartment(),
    new FinanceDepartment()
};

var visitor = new VisitorFromOutside();

foreach (var dept in departments)
{
    dept.Accept(visitor);
}

Final Thoughts

The Visitor Pattern is like welcoming guests into a company — some are here for interviews, others to review finances, others for partnerships.

Each department knows how to interact with the visitor, but the business logic lives in the visitor class. It's a powerful way to grow behaviour without bloating your object models.