Visitor Pattern in C#
Tags: beginners, programming, tutorial, csharp
Published: 2022-09-14 16:08:44

_Originally posted [here](https://dfordebugging.wordpress.com/2022/09/14/visitor-pattern-in-c/)_

The **Visitor** is a behavioural design pattern that lets us separate algorithms from the entities on which they operate.

The **Visitor** pattern lets us operate on objects by representing that operation as an object unto itself. We can then operate on said objects without changing the definitions or functionality of those objects.

You can find the example code of this post, on [GitHub](https://github.com/Kalkwst/DesignPatterns).

## Conceptualizing the Problem

Imagine that we are working on an application with geographical information structured as one humongous graph. The graph has different kinds of nodes. Some represent complex entities such as cities, and more granular things like shops and businesses, sightseeing areas, etc. The nodes are connected if there's a road between the real-life objects they represent. Under the hood, each node type is represented by its class.

![Visitor Graph 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kav9rlkyoczeyj8cpxow.PNG)

Now we need to implement a mechanism to export the graph into XML format. At first, the job seemed pretty straightforward. We can add an export method to each node class and then leverage recursion to go over each node of the graph, executing the export method. The solution is straightforward and elegant: thanks to polymorphism, we aren't coupling the code called the export method to concrete classes of nodes.

Unfortunately, the system architect refused to allow us to alter existing node classes. The code is already in production, and a potential bug in the implementation can crash the application. 

![Visitor Graph 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/njqw5hx6vdvuowxa9d1a.PNG)

Besides, it doesn't make sense to have the XML export code within the node classes. The primary job of these classes was to work with geodata. The XML export behaviour would look alien there.

There's another reason for the refusal. It is highly likely that after the implementation of this feature, Mr Smithers from the marketing department would ask us to provide the functionality to export into a different format, or request some other weird stuff. This would force us to change those precious classes again.

The **Visitor** pattern suggests that we place the new behaviour into a separate class called *visitor*, instead of trying to integrate it into existing classes. The original object that had to perform the behaviour is now passed to one of the visitor's methods as an argument., providing the method access to all necessary data contained within the object.

In our case, the actual implementation will probably be a little different across various node classes. Thus, the visitor class may define not one, but a set of methods, each of which could take arguments of different types:

```csharp
public class ExportVisitor : IVisitor
{
	public string ExportCity(City city) {...}
	public string ExportRestaurant(Restaurant restaurant) {...}
	public string ExportSightseeing(Sightseeing sightseeing) {...}
	//...
}
```

The problem now is how to deal with the whole graph. These methods have different signatures, so we can't use polymorphism. To pick a proper method that's able to process a given object, we'd need to check its class.

```csharp
foreach(Node node in graph)
{
	if (node is City)
		visitor.ExportCity((City)node);
	if (node is Restaurant)
		visitor.ExportRestaurant((Restaurant)node);
	//...
}
```

Why don't we use method overloading? We can give all methods the same name, even if they support different sets of parameters. C# supports method overloading after all. Unfortunately, it won't help us. Since the exact class of the node is unknown in advance, the compiler won't be able to determine the correct method to execute. It will default to the method that takes an instance of the base `Node` class.

However, the **Visitor** pattern addresses this problem. It uses a technique called **Double Dispatch**, which helps to execute the proper method on an object without cumbersome conditionals. Instead of letting the client select the method to call, we delegate this choice to the visitor as an argument. Since the objects know their classes, they can pick a proper method for the visitor.

```csharp
foreach(Node node in graph)
	node.Accept(visitor);
//...

public class City 
{
	public string Accept(Visitor v)
	{
		v.ExportCity(this);
		//...
	}
}

public class Restaurant
{
	public string Accept(Visitor v)
	{
		v.ExportRestaurant(this);
		//...
	}
}
```

Now, if we extract a common interface for all visitors, all existing nodes can work with any visitor we introduce into the application. If we find ourselves introducing a new behaviour related to nodes, all we have to do is to implement a new visitor class.

## Structuring the Visitor Pattern

In its base implementation the Visitor pattern has five participants:

![Visitor Class Diagram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bf2rc9akf2chrca8du7c.PNG)

- **Visitor**: The **Visitor** interface declares a set of visiting methods that can take concrete elements of an object structure as arguments. These methods may have the same name, but the type of their parameters must be different.
- **Concrete Visitor**: Each **Concrete Visitor** implements several versions of the same behaviours, tailored for different concrete element classes.
- **Element**: The **Element** interface declares a method for accepting visitors. This method should have one parameter declared with the type of visitor interface.
- **Concrete Element**: Each **Concrete Element** must implement the accept method. The purpose of this method is to redirect the call to the proper visitor's method corresponding to the current element class. Note that if a base element class implements this method, all subclasses must still override this method and call the appropriate method on the visitor object.
- **Client**: The **Client** usually represents a collection or some other complex object (for example, a **Composite** tree). Usually, clients aren't aware of all the concrete element classes since they work with objects from the collection via some abstract interface.

To demonstrate how the visitor pattern work, we will create an application that creates geometric shapes and export them into XML format.

First, we will create the **Element** participant, represented by the `IShape` interface.

```csharp
using Visitor.Visitor;

namespace Visitor.Shapes
{
    /// <summary>
    /// Common shape interface
    /// </summary>
    public interface IShape
    {
        public void Move(int x, int y);
        public void Draw();
        public string Accept(IVisitor visitor);
    }
}
```

We also need some **Concrete Elements** which represent the various shapes supported by the application. Note that each element accepts an `IVisitor` and then triggers the appropriate visitor method. This is called **Double Dispatch** and we will discuss it further a bit later.

```csharp
using Visitor.Visitor;

namespace Visitor.Shapes
{
    public class Dot : IShape
    {
        public Guid Id { get; set; }
        public int X { get; set; }
        public int Y { get; set; }

        public Dot()
        {
        }

        public Dot(Guid id, int x, int y)
        {
            Id = id;
            X = x;
            Y = y;
        }

        public string Accept(IVisitor visitor)
        {
            return visitor.VisitDot(this);
        }

        public void Draw()
        {
            Console.WriteLine($"Drawing dot-{Id} at ({X},{Y})");
        }

        public void Move(int x, int y)
        {
            X += x;
            Y += y;
        }
    }
}
```

```csharp
using Visitor.Visitor;

namespace Visitor.Shapes
{
    public class Circle : IShape
    {
        public Guid Id { get; set; }
        public int X { get; set; }
        public int Y { get; set; }
        public int Radius { get; }

        public Circle(Guid id, int x, int y, int radius)
        {
            Id = id;
            X = x;
            Y = y;
            Radius = radius;
        }

        public new string Accept(IVisitor visitor)
        {
            return visitor.VisitCircle(this);
        }

        public void Move(int x, int y)
        {
            X += x;
            Y += y;
        }

        public void Draw()
        {
            Console.WriteLine($"Drawing circle-{Id} at ({X},{Y}) with radius {Radius}");
        }
    }
}
```

```csharp
using Visitor.Visitor;

namespace Visitor.Shapes
{
    public class Rectangle : IShape
    {
        public Guid Id { get; set; }

        public int X { get; set; }
        public int Y { get; set; }

        public int Width { get; set; }
        public int Height { get; set; }

        public Rectangle(Guid id, int x, int y, int width, int height)
        {
            Id = id;
            X = x;
            Y = y;
            Width = width;
            Height = height;
        }

        public string Accept(IVisitor visitor)
        {
            return visitor.VisitRectangle(this);
        }

        public void Draw()
        {
            Console.WriteLine($"Drawing rectangle-{Id} at ({X},{Y}) with width {Width} and height {Height}");
        }

        public void Move(int x, int y)
        {
            X += x;
            Y += y;
        }
    }
}
```

We will also add the `ComplexShape` class that will represent a compound shape, containing other shapes:

```csharp
using Visitor.Visitor;

namespace Visitor.Shapes
{
    public class ComplexShape : IShape
    {
        public Guid Id { get; set; }
        public List<IShape> children = new();

        public int X { get; set; }
        public int Y { get; set; }

        public ComplexShape(Guid id)
        {
            Id = id;
        }

        public void Add(IShape shape)
        {
            children.Add(shape);
        }

        public string Accept(IVisitor visitor)
        {
            return visitor.VisitComplex(this);
        }

        public void Draw()
        {
            Console.WriteLine($"Drawing complex-{Id} at ({X},{Y})");
        }

        public void Move(int x, int y)
        {
            X += x;
            Y += y;
        }
    }
}
```

Now, we can define our **Visitor** participant. This will be an interface that will define various visit methods, one for each concrete element.

```csharp
using Visitor.Shapes;

namespace Visitor.Visitor
{
    public interface IVisitor
    {
        public string VisitDot(Dot dot);
        public string VisitCircle(Circle circle);
        public string VisitRectangle(Rectangle rectangle);
        public string VisitComplex(ComplexShape complexShape);
    }
}
```

Finally, we will implement our **Concrete Visitor**, in this case, an `XMLExportVisitor`. This class will implement the `Visit` methods of the `IVisitor` interface, and create the XML elements for each element:

```csharp
using System.Text;
using System.Xml.Linq;
using Visitor.Shapes;

namespace Visitor.Visitor
{
    public class XMLExportVisitor : IVisitor
    {
        public string Export(params IShape[] shapes)
        {
            StringBuilder builder = new StringBuilder();
            builder.AppendLine("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
            builder.AppendLine("<shapes>");

            foreach (IShape shape in shapes)
                builder.AppendLine(shape.Accept(this));

            builder.AppendLine("</shapes>");
            return XMLFormatter(builder.ToString());
        }

        public string VisitCircle(Circle circle)
        {
            return $@"<circle> 
                          <id>{circle.Id}</id> 
                          <x>{circle.X}</x> 
                          <y>{circle.Y}</y> 
                          <radius>{circle.Radius}</radius> 
                      </circle>";
        }

        public string VisitComplex(ComplexShape complexShape)
        {
            return $@"<complex> 
                          <id>{complexShape.Id}</id> 
                          {VisitChildren(complexShape)}
                      </complex>";

        }

        public string VisitDot(Dot dot)
        {
            return $@"<dot> 
                      <id>{dot.Id}</id> 
                      <x>{dot.X}</x> 
                      <y>{dot.Y}</y> 
                      </dot>";
        }

        public string VisitRectangle(Rectangle rectangle)
        {
            return $@"<rectangle> 
                          <id>{rectangle.Id}</id> 
                          <x>{rectangle.X}</x> 
                          <y>{rectangle.Y}</y> 
                          <width>{rectangle.Width}</width> 
                          <height>{rectangle.Height}</height> 
                      </rectangle>";
        }

        private string VisitChildren(ComplexShape shape)
        {
            StringBuilder stringBuilder = new StringBuilder();

            foreach (IShape child in shape.children)
            {
                string childXML = child.Accept(this);
                stringBuilder.AppendLine(childXML);
            }

            return stringBuilder.ToString();
        }

        private string XMLFormatter(string xml)
        {
            XDocument doc = XDocument.Parse(xml);
            return doc.ToString();
        }
    }
}
```

To put this all together, in our `Main()` method we will define a few shapes and then use the `XMLExportVisitor` to export them in XML format:

```csharp
using Visitor.Shapes;
using Visitor.Visitor;

public class Program
{
    public static void Main(string[] args)
    {
        Dot dot = new Dot(Guid.NewGuid(), 10, 12);
        Circle circle = new Circle(Guid.NewGuid(), 23, 15, 21);
        Rectangle rectangle = new Rectangle(Guid.NewGuid(), 10, 17, 20, 32);

        ComplexShape complex = new ComplexShape(Guid.NewGuid());
        complex.Add(dot);
        complex.Add(circle);
        complex.Add(rectangle);

        ComplexShape complexShapeDot = new ComplexShape(Guid.NewGuid());
        complexShapeDot.Add(dot);
        complex.Add(complexShapeDot);

        Export(dot, circle, complex);

		Console.ReadKey();
    }

    private static void Export(params IShape[] shapes)
    {
        XMLExportVisitor xmlVisitor = new XMLExportVisitor();
        Console.WriteLine(xmlVisitor.Export(shapes));
    }
}
```

If we run the application, we will see the shapes exported in XML format. Below is the output:

![Visitor Output](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c4re3j6qybd9zrj2tpg0.PNG)

## Double Dispatch and the Visitor Pattern

Double dispatch is a technique that we can use, to control how communication flows between two objects. 

Suppose we are creating an agronomist probe that routinely travels across various and checks soil composition. However, testing the soil composition is different between locations, due to differences in the types of soil found in each location.

Let's model 3 types of soil for simplicity:

```csharp
namespace VisitorDoubleDispatch.SingleDispatch.Soils
{
    public interface ISoil
    {
        public void DisplayName();
    }
}

namespace VisitorDoubleDispatch.SingleDispatch.Soils
{
    public class Loam : ISoil
    {
        public void DisplayName()
        {
            Console.WriteLine("Loam");
        }
    }
}

namespace VisitorDoubleDispatch.SingleDispatch.Soils
{
    public class Peat : ISoil
    {
        public void DisplayName()
        {
            Console.WriteLine("Peat");
        }
    }
}

namespace VisitorDoubleDispatch.SingleDispatch.Soils
{
    public class Podzol : ISoil
    {
        public void DisplayName()
        {
            Console.WriteLine("Podzol");
        }
    }
}
```

Then we will implement the methods for probing the soil:

```csharp
using VisitorDoubleDispatch.SingleDispatch.Soils;

namespace VisitorDoubleDispatch.SingleDispatch.Probe
{
    public interface IProbe
    {
        public void Visit(Loam loam);
        public void Visit(Peat peat);
        public void Visit(Podzol podzol);
        public void Visit(ISoil soil);
    }
}
```

```csharp
using VisitorDoubleDispatch.SingleDispatch.Soils;

namespace VisitorDoubleDispatch.SingleDispatch.Probe
{
    public class SoilProbe : IProbe
    {
        public void Visit(Loam loam)
        {
            Console.WriteLine("Deploying tools specific to loam");
        }

        public void Visit(Peat peat)
        {
            Console.WriteLine("Deploying tools specific to peat");
        }

        public void Visit(Podzol podzol)
        {
            Console.WriteLine("Deploying tools specific to podzol");
        }

        public void Visit(ISoil soil)
        {
            Console.WriteLine("Cannot probe unknown soil");
        }
    }
}

```

Now we can deploy our probe:

```csharp
using VisitorDoubleDispatch.SingleDispatch.Probe;
using VisitorDoubleDispatch.SingleDispatch.Soils;

namespace VisitorDoubleDispatch.SingleDispatch
{
    public class SingleDispatchRunner
    {
        public static void Run()
        {
            ISoil podzol = new Podzol();
            ISoil peat = new Peat();
            ISoil loam = new Loam();

            IProbe probe = new SoilExplorer();

            List<ISoil> soilsToVisit = new List<ISoil>();

            soilsToVisit.Add(podzol);
            soilsToVisit.Add(peat);
            soilsToVisit.Add(loam);

            foreach (ISoil soil in soilsToVisit)
                probe.Visit(soil);
        }
    }
}
```

And the output will be:

![Single Dispatch Output](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xtf1qb1dylqo7a4sdw1h.PNG)

### Thinking as a Compiler
Let's for a moment pretend that we are the compiler, and decide to compile the following code:

```csharp
public void DisplaySoilName(ISoil soil)
{
	soil.DisplayName();
}
```

The `DisplayName()` method is defined in the `ISoil` interface. But wait for a second, three classes implement the interface. Can we safely decide which of the implementations to call here? It doesn't look so. The only way to know for sure is to launch the application and check the class of an object passed to the method. The only thing we know is that object **will have** an implementation of the `DisplayName()` method.

So the resulting compiled code will be checking the class of the object passed to the `soil` parameter and picking the `DisplayName` implementation of the appropriate class.

This is called **late** or **dynamic** binding. **Late** because the object and its implementations are linked at runtime. **Dynamic** because every new object might need to be linked to a different implementation.

Now, let's compile the following code:

```csharp
IProbe probe = new SoilExplorer();

foreach (ISoil soil in soilsToVisit)
    probe.VisitSoil(soil);
```

Everything is clear with the first line: the `SoilExplorer` class doesn't have a custom constructor, so we just instantiate an object. What about the `VisitSoil` call? The `SoilExplorer` class has four methods with the same name that differ with parameter types. Which one to call? Looks like we're going to need a dynamic binding here as well.

But there's another problem. What if there's a soil class that doesn't have any appropriate method in the `SoilExplorer` class? For instance, a `Clay` soil. The compiler can't guarantee that the correct overloaded method exists. An ambiguous situation arises that a compiler can't allow. Therefore, compiler development teams use a safe path and use the early binding for overloaded methods.

This is called **early** or **static** binding. **Early** because it happens at compile time before the application is launched. **Static** because it can't be altered at runtime.

Let's return to our example. We're sure that the incoming argument will be of the `Soil` hierarchy: either the `ISoil` class or one of its subclasses. We also know that the `SoilExplorer` class has a basic implementation of the `VisitSoil` that supports the `ISoil` interface: `VisitSoil(ISoil soil)`.

That's the only implementation that can be safely linked to a given code without making things ambiguous. That's why even if we pass a `Podzol` object into `VisitSoil`, the exporter will still call the `VisitSoil(ISoil soil)` method.

### Double Dispatch

**Double Dispatch** is a technique that allows using dynamic binding alongside overloaded methods.

We can simulate the second level of polymorphism using the **Visitor** pattern.

First, we need to implement the `Accept` method in the `ISoil` classes. The purpose of the `Accept` method is to give the `IProbe` more information about which `ISoil` to `Visit` via `this`.

```csharp
using VisitorDoubleDispatch.DoubleDispatch.Probes;

namespace VisitorDoubleDispatch.DoubleDispatch.Soils
{
    public interface ISoil
    {
        public void Accept(IProbe probe);
        public void DisplayName();
    }
}
```

```csharp
using VisitorDoubleDispatch.DoubleDispatch.Probes;

namespace VisitorDoubleDispatch.DoubleDispatch.Soils
{
    public class Loam : ISoil
    {
        public void Accept(IProbe probe)
        {
            probe.Visit(this);
        }

        public void DisplayName()
        {
            Console.WriteLine("Loam");
        }
    }
}

using VisitorDoubleDispatch.DoubleDispatch.Probes;

namespace VisitorDoubleDispatch.DoubleDispatch.Soils
{
    public class Peat : ISoil
    {
        public void Accept(IProbe probe)
        {
            probe.Visit(this);
        }

        public void DisplayName()
        {
            Console.WriteLine("Peat");
        }
    }
}

using VisitorDoubleDispatch.DoubleDispatch.Probes;

namespace VisitorDoubleDispatch.DoubleDispatch.Soils
{
    public class Podzol : ISoil
    {
        public void Accept(IProbe probe)
        {
            probe.Visit(this);
        }

        public void DisplayName()
        {
            Console.WriteLine("Podzol");
        }
    }
}
```

Then, we can use the `ISoil.Accept` method to probe the different types of soils. Note that the `Accept` method adds an extra layer of indirection. It is there to explicitly tell the `Probe` which `ISoil` implementation to visit.

```csharp
using VisitorDoubleDispatch.DoubleDispatch.Probes;
using VisitorDoubleDispatch.DoubleDispatch.Soils;

namespace VisitorDoubleDispatch.DoubleDispatch
{
    public class DoubleDispatchRunner
    {
        public static void Run()
        {
            ISoil loam = new Loam();
            ISoil peat = new Peat();
            ISoil podzol = new Podzol();

            IProbe probe = new SoilProbe();

            List<ISoil> soilsToBeVisited = new();

            soilsToBeVisited.Add(loam);
            soilsToBeVisited.Add(peat);
            soilsToBeVisited.Add(podzol);

            foreach (ISoil soil in soilsToBeVisited)
                soil.Accept(probe);
        }
    }
}
```

And the output will be:

![Visitor Double Dispatch Output](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uaaos90t0yuhdjld3ucj.PNG)

We have effectively broken down the initial `probe.Visit(soil)` call into two distinct layers. The  `soil.Accept(probe)` call utilizes polymorphism to figure out which `ISoil` implementation to call, and the `probe.Visit(this)` call informs the compiler about the specific class to bind to.

## Pros and Cons of Visitor Pattern
|||
|:--|:--|
|✔ We can introduce a new behaviour that can work with objects of different classes without changing these classes, thus satisfying the _Open/Closed Principle_|❌ We need to update all visitors each time a class gets added to or removed from the element hierarchy|
|✔ We can move multiple versions of the same behaviour into the same class, thus satisfying the _Single Responsibility Principle_|❌ Visitors might lack the necessary access to the private fields and methods of the elements that they’re supposed to work with|
|✔ A visitor object can accumulate some useful information while working with various objects. This might be handy when we want to traverse some complex object structures, such as an object tree, and apply the visitor to each object of this structure.||

## Relations with Other Patterns
- We can treat the **Visitor** pattern as a more powerful version of the **Command** pattern. Its objects can execute operations over various objects of different classes.
- We can use the **Visitor** pattern to execute an operation over the entire **Composite** tree.
- We can use the **Visitor** pattern along with the **Iterator** pattern to traverse a complex data structure and execute some operations over its elements, even if they all have different classes.

## Final Thoughts
In this article, we have discussed what is the Visitor pattern, when to use it and what are the pros and cons of using this design pattern. We then examined what is the Double Dispatch technique and how to implement it using the Visitor pattern. Finally, we saw how the Visitor pattern relates to other classic design patterns.

The Visitor design pattern is helpful in many ways and is quite flexible if appropriately used. However, it's worth noting that the Visitor pattern, along with the rest of the design patterns presented by the Gang of Four, is not a panacea or a be-all-end-all solution when designing an application. Once again it's up to the engineers to consider when to use a specific pattern. After all these patterns are useful when used as a precision tool, not a sledgehammer.