Visitor Pattern in C# – 5 Versions

Visitor Pattern in C# – 5 Versions

Visitor Pattern in C# – 5 Versions

Abstract: In this article, you will find a tutorial that describes the Visitor pattern in C#. After a discussion of the “Classic Visitor”, which is a version proposed by GoF and often mentioned in the literature, we will look at the other four versions of the Visitor Pattern which can be viewed as “modern-C#-enabled” alternative versions of Visitor Pattern.

 

Download Code 62.97 KB 2 downloads

 

Introduction

This is a tutorial article describing Visitor Pattern in C#. The intended audience is Intermediate level C# programmers and above.
Visitor Pattern is one of the most complicated patterns out of 23 GoF patterns. In C#, it comes in several versions. Here, we are going to describe it in five versions:

  1. Visitor Pattern in C# – Version 1 – Classic Visitor
  2. Visitor Pattern in C# – Version 2 – Dynamic Visitor
  3. Visitor Pattern in C# – Version 3 – Reflective Visitor
  4. Visitor Pattern in C# – Version 4 – Reflective-Extension Visitor
  5. Visitor Pattern in C# – Version 5 – Generics Visitor

The problem we are trying to solve

First, let us try to understand which problem we are trying to solve with this pattern and what limitations of classical OO design are. Let us look at the classic OO design on Picture 1-1 and Code 1-1.

 

public abstract class Element
{
    public int Attribute1 = 0;
    public int Attribute2 = 0;

    abstract public void V1();

    abstract public void V2();

    abstract public void V3();
}

public class ElementA : Element
{
    public ElementA()
    {
    }

    public int Attribute3 = 0;

    public override void V1()
    {
    }

    public override void V2()
    {
    }

    public override void V3()
    {
    }
}

public class ElementB : Element
{
    public ElementB()
    {
    }

    public int Attribute3 = 0;

    public override void V1()
    {
    }

    public override void V2()
    {
    }

    public override void V3()
    {
    }
}

Problem or better to say, limitations we see with this solution are:

  • Data and algorithms (methods V1, V2, etc.) are coupled in this approach. It might be useful sometimes to try to separate them
  • Adding new operations ( for example V4) is not easy, without changing existing class structures. That is contrary to the open/close principle. It would be desirable to be possible to add new operations (methods) without changing class structure.
  • In the same place, different methods ( for example, V1 and V2) can address completely different and unrelated functionality/concerns. For example, V1 can be concerned with generating .pdf, while V2 can be concerned with generating Html. That is contrary to the principle of separation of concerns.

 

Visitor Pattern

Visitor pattern addresses the above concerns/limitations by dividing data and operations into separate classes. Data is kept in Element/Elements classes, while operations are kept in Visitor/Visitors classes, where each specific Visitor can address separate concerns. Extending of operations on Elements can be easily achieved by creating new Visitor classes.
The key part of this pattern is the design solution that enables the Visitor object to perform operations on the Element. We say that “Visitor visits the Element” to perform an operation on the Element.

If we look into our class diagram Picture1-1, we see that we for object ElementA have method V1, so operation invocation will look something like:

ElementA elementa=new ElementA();
elementa.V1();

In the Visitor pattern, the operation performed with method V1() will be encapsulated in object Visitor1, operations performed with method V2() will be encapsulated in object Visitor2, etc. The same operation invocation will look now like this:

 

ElementA elementa=new ElementA();
Visitor1 visitor1=new Visitor1();
visitor1.visit(elementa);

The situation does not end here. Problem is that we will have several Element and Visitor objects, which we often approach via base class/interface. Then appears a problem of dispatching the appropriate method. “Dispatch” is a problem of finding out which concrete method to call.

C#, like most OO languages, supports “Singe dispatch” in form of virtual functions calls. That is so-called “dynamic binding”. Based on the type of object in question, dynamically in runtime C# will invoke the appropriate virtual function from a virtual method table.

But sometimes, that is not enough, and “Multiple dispatch” is needed. Multiple dispatch is a problem of finding which concrete method to call based on runtime types of multiple objects.

 

Visitor Pattern in C# – Version 1 – Classic Visitor

Classic Visitor version of Visitor Pattern is most often found in the literature. In the classic version of the visitor pattern, the pattern is based on the “Double dispatch” mechanism of C#. The double dispatch mechanism used in this solution is based on two features of C#: 1) ability to dynamically bind concrete virtual method based on the type of object; 2) ability to resolve overloaded methods to the concrete method based on the type of argument.

Here is what the class diagram looks like for sample code:

 

Here is the code of this:

 

public abstract class Element
{
    public abstract void Accept(IVisitor visitor);
}

public class ElementA : Element
{
	public int Id = 0;

	public ElementA(int Id)
	{
		this.Id = Id;
	}

	public override void Accept(IVisitor visitor)     //(2)
	{
		visitor.Visit(this);
	}
}

public class ElementB : Element
{
	public int Id = 0;

	public ElementB(int Id)
	{
		this.Id = Id;
	}
	public override void Accept(IVisitor visitor)
	{
		visitor.Visit(this);
	}
}

public interface IVisitor
{
	void Visit(ElementA ElemA);
	void Visit(ElementB ElemB);
}

public class Visitor1 : IVisitor    //(3)
{
	public virtual void Visit(ElementA ElemA)  //(4)
	{
		Console.WriteLine("{0} with Id={1} visited by {2}",
			ElemA.GetType().Name, ElemA.Id, this.GetType().Name);
	}
	public virtual void Visit(ElementB ElemB)
	{
		Console.WriteLine("{0} with Id={1} visited by {2}",
			ElemB.GetType().Name, ElemB.Id, this.GetType().Name);
	}
}

public class Visitor2 : IVisitor
{
	public virtual void Visit(ElementA ElemA)
	{
		Console.WriteLine("{0} with Id={1} visited by {2}",
			ElemA.GetType().Name, ElemA.Id, this.GetType().Name);
	}
	public virtual void Visit(ElementB ElemB)
	{
		Console.WriteLine("{0} with Id={1} visited by {2}",
			ElemB.GetType().Name, ElemB.Id, this.GetType().Name);
	}
}

class Client
{
	static void Main(string[] args)
	{
		//--single element, explicit call-------------------------
		ElementA element0 = new ElementA(0);
		Visitor1 vis0 = new Visitor1();

		vis0.Visit(element0);  //(0) works

		//--single element, base class call-----------------------
		Element element = new ElementA(1);
		IVisitor visitor = new Visitor1();

		//visitor.Visit(element);   //(5) will not compile

		element.Accept(visitor);  //(1)

		//--collection of elements-----------------
		List<IVisitor> listVis = new List<IVisitor>();
		listVis.Add(new Visitor1());
		listVis.Add(new Visitor2());

		List<Element> list = new List<Element>();
		list.Add(new ElementA(2));
		list.Add(new ElementB(3));
		list.Add(new ElementA(4));
		list.Add(new ElementB(5));

		foreach (IVisitor vis in listVis)
			foreach (Element elem in list)
			{
				elem.Accept(vis);
			}

		Console.ReadLine();
	}
}

Here is the result of sample execution:

 

Please note that in (0), when the Visitor invoked over explicit classes, all works. We say that “Visitor visits the Element” to perform an operation on the Element.

But, in (5), when we try to invoke the visitor over base classes/interface, we can not compile. The compiler can not resolve which method to call. That is why we need all this magic with “Double dispatch” to properly resolve which concrete method to call.

In (1) we have proper invocation. What is happening is:

  1. in (1) we have dynamic binding to (2)
  2. in (2) we have dynamic binding to (3)
  3. in (2) we have overload resolution to (4)

Because in (2) we have a double resolution, that is the reason why it is called “Double Dispatch”.

Limitation of this solution. As with any solution, this has some limitations/unwanted side effects:

  • There is a strong cyclic dependency between class hierarchies Elements and Visitor. That can be a problem if hierarchies need to be frequently updated
  • Note that in (4) for Visitor to access data attribute Id of Element, that attribute needs to be public. That a bit breaks the encapsulation principle. For example in our first solution, class diagram Picture1-1, method V1() could operate on private members of class Element. In C++ that can be solved by usage of the “friend class” paradigm, but that is not the case in C#.

 

Visitor Pattern in C# – Version 2 – Dynamic Visitor

Dynamic Visitor version of Visitor Pattern is based on C# support of dynamic dispatch. That is the ability of language to dynamically dispatch, that is to make concrete call decisions at runtime. We will cast the variable to “dynamic” and in that way defer dispatch decisions until runtime. We again have “Double dispatch” since we are dispatching to the concrete method based on types of two objects, just used language mechanism is different.

Here is what the class diagram looks like for sample code:

 

Here is the code of this:

public abstract class AElement
{
}

public class ElementA : AElement
{
	public int Id = 0;

	public ElementA(int Id)
	{
		this.Id = Id;
	}
}

public class ElementB : AElement
{
	public int Id = 0;

	public ElementB(int Id)
	{
		this.Id = Id;
	}
}

public interface IVisitor
{
	void Visit(ElementA ElemA);
	void Visit(ElementB ElemB);
}

public class Visitor1 : IVisitor    //(2)
{
	public virtual void Visit(ElementA ElemA)  //(3)
	{
		Console.WriteLine("{0} with Id={1} visited by {2}",
			ElemA.GetType().Name, ElemA.Id, this.GetType().Name);
	}
	public virtual void Visit(ElementB ElemB)
	{
		Console.WriteLine("{0} with Id={1} visited by {2}",
			ElemB.GetType().Name, ElemB.Id, this.GetType().Name);
	}
}

public class Visitor2 : IVisitor
{
	public virtual void Visit(ElementA ElemA)
	{
		Console.WriteLine("{0} with Id={1} visited by {2}",
			ElemA.GetType().Name, ElemA.Id, this.GetType().Name);
	}
	public virtual void Visit(ElementB ElemB)
	{
		Console.WriteLine("{0} with Id={1} visited by {2}",
			ElemB.GetType().Name, ElemB.Id, this.GetType().Name);
	}
}

class Client
{
	static void Main(string[] args)
	{
		//--single element-------------------------
		AElement element = new ElementA(1);
		IVisitor visitor = new Visitor1();

		visitor.Visit((dynamic)element); //(1)

		//--collection of elements-----------------
		List<IVisitor> listVis = new List<IVisitor>();
		listVis.Add(new Visitor1());
		listVis.Add(new Visitor2());

		List<AElement> list = new List<AElement>();
		list.Add(new ElementA(2));
		list.Add(new ElementB(3));
		list.Add(new ElementA(4));
		list.Add(new ElementB(5));

		foreach (IVisitor vis in listVis)
			foreach (AElement elem in list)
			{
				vis.Visit((dynamic)elem);
			}

		Console.ReadLine();
	}
}

Here is the result of sample execution:

 

In (1) we have a new invocation call. Due to the nature of how dynamic objects work, the resolution is deferred until runtime. Then we have first dynamic binding based on the type of Visitor to (2), then dynamic resolution to (3) based on the type of Element that is discovered dynamically at runtime.

Limitation of this solution. As with any solution, this has some limitations/unwanted side effects:

  • There is a strong cyclic dependency between class hierarchies, Elements, and Visitors. That can be a problem if hierarchies need to be frequently updated.
  • Note that for Visitor to access data attribute Id of Element, that attribute needs to be public. That breaks the encapsulation principle a bit. For example, in our first solution, class diagram Picture1-1, method V1() could operate on private members of class Element.
  • Usage of “dynamic” object brings us performance impact.

 

Visitor Pattern in C# – Version 3 – Reflective Visitor

Reflective Visitor version of Visitor Pattern is based on the usage of C# Reflection technology to discover object types in runtime and perform explicit method dispatch based on types found. We again have “Double dispatch” since we are dispatching to a concrete method based on types of two objects, just used language mechanism is different.

Here is what the class diagram looks like for sample code:

 

Here is the code of this:

 

public abstract class AElement
{
}

public class ElementA : AElement
{
	public int Id = 0;

	public ElementA(int Id)
	{
		this.Id = Id;
	}
}

public class ElementB : AElement
{
	public int Id = 0;

	public ElementB(int Id)
	{
		this.Id = Id;
	}
}

public abstract class AVisitor
{
    public void Visit(AElement Elem)  //(2)
    {
        if (Elem is ElementA)
        {
            Visit((ElementA)Elem);
        };
        if (Elem is ElementB)
        {
            Visit((ElementB)Elem);
        };
    }
    public abstract void Visit(ElementA ElemA);
    public abstract void Visit(ElementB ElemB);
}

public class Visitor1 : AVisitor
{
	public override void Visit(ElementA ElemA)  //(3)
	{
		Console.WriteLine("{0} with Id={1} visited by {2}",
			ElemA.GetType().Name, ElemA.Id, this.GetType().Name);
	}
	public override void Visit(ElementB ElemB)
	{
		Console.WriteLine("{0} with Id={1} visited by {2}",
			ElemB.GetType().Name, ElemB.Id, this.GetType().Name);
	}
}

public class Visitor2 : AVisitor
{
	public override void Visit(ElementA ElemA)
	{
		Console.WriteLine("{0} with Id={1} visited by {2}",
			ElemA.GetType().Name, ElemA.Id, this.GetType().Name);
	}
	public override void Visit(ElementB ElemB)
	{
		Console.WriteLine("{0} with Id={1} visited by {2}",
			ElemB.GetType().Name, ElemB.Id, this.GetType().Name);
	}
}

class Client
{
    static void Main(string[] args)
    {
        //--single element-------------------------
        AElement element = new ElementA(1);
        AVisitor visitor = new Visitor1();

        visitor.Visit(element); //(1)

        //--collection of elements-----------------
        List<AVisitor> listVis = new List<AVisitor>();
        listVis.Add(new Visitor1());
        listVis.Add(new Visitor2());

        List<AElement> list = new List<AElement>();
        list.Add(new ElementA(2));
        list.Add(new ElementB(3));
        list.Add(new ElementA(4));
        list.Add(new ElementB(5));

        foreach (AVisitor vis in listVis)
            foreach (AElement elem in list)
            {
                vis.Visit(elem);
            }

        Console.ReadLine();
    }
}

Here is the result of sample execution:

 

In (1) we have a new invocation call. Even in compile-time, it is resolved to the method (2). Then during runtime, using Reflection, the type of argument is resolved and invocation passed to (3).

Limitation of this solution. As with any solution, this has some limitations/unwanted side effects:

  • There is a strong cyclic dependency between class hierarchies, Elements, and Visitors. That can be a problem if hierarchies need to be frequently updated.
  • Note that for Visitor to access data attribute Id of Element, that attribute needs to be public. That breaks the encapsulation principle a bit. For example, in our first solution, class diagram Picture1-1, method V1() could operate on private members of class Element.
  • Note that in (2), every class inherited from AElement is explicitly mentioned and checked for type. Missing some type might be a problem for implementation. One possible solution would be to discover all types in assembly by using Reflection and automatically dispatching to all classes that inherit from AElement. But, we are not going to do that here.

 

Visitor Pattern in C# – Version 4 – Reflective-Extension Visitor

Reflective-Extension Visitor version of Visitor Pattern is based on 1)usage of C# Reflection technology to discover object types in runtime and perform explicit method dispatch based od types found; 2) usage of Extension methods. This version is very similar to version “Reflective Visitor”, but since it is mentioned in literature elsewhere, we also list it here as a separate variant. We again have “Double dispatch” since we are dispatching to a concrete method based on types of two objects, just used language mechanism is different.

Here is what the class diagram looks like for sample code:

 

Here is the code of this:

 

public abstract class AElement
{
}

public class ElementA : AElement
{
	public int Id = 0;

	public ElementA(int Id)
	{
		this.Id = Id;
	}
}

public class ElementB : AElement
{
	public int Id = 0;

	public ElementB(int Id)
	{
		this.Id = Id;
	}
}

public abstract class AVisitor
{
    public abstract void Visit(ElementA ElemA);
    public abstract void Visit(ElementB ElemB);
}

public static class AVisitorExtensions
{
    public static void Visit<T>(this T vis, AElement Elem)
        where T : AVisitor               //(2)
    {
        if (Elem is ElementA)
        {
            vis.Visit((ElementA)Elem);    //(3)
        };
        if (Elem is ElementB)
        {
            vis.Visit((ElementB)Elem);
        };
    }
}

public class Visitor1 : AVisitor
{
	public override void Visit(ElementA ElemA)  //(4)
	{
		Console.WriteLine("{0} with Id={1} visited by {2}",
			ElemA.GetType().Name, ElemA.Id, this.GetType().Name);
	}
	public override void Visit(ElementB ElemB)
	{
		Console.WriteLine("{0} with Id={1} visited by {2}",
			ElemB.GetType().Name, ElemB.Id, this.GetType().Name);
	}
}

public class Visitor2 : AVisitor
{
	public override void Visit(ElementA ElemA)
	{
		Console.WriteLine("{0} with Id={1} visited by {2}",
			ElemA.GetType().Name, ElemA.Id, this.GetType().Name);
	}
	public override void Visit(ElementB ElemB)
	{
		Console.WriteLine("{0} with Id={1} visited by {2}",
			ElemB.GetType().Name, ElemB.Id, this.GetType().Name);
	}
}

class Client
{
    static void Main(string[] args)
    {
        //--single element-------------------------
        AElement element = new ElementA(1);
        AVisitor visitor = new Visitor1();

        visitor.Visit(element);      //(1)

        //--collection of elements-----------------
        List<AVisitor> listVis = new List<AVisitor>();
        listVis.Add(new Visitor1());
        listVis.Add(new Visitor2());

        List<AElement> list = new List<AElement>();
        list.Add(new ElementA(2));
        list.Add(new ElementB(3));
        list.Add(new ElementA(4));
        list.Add(new ElementB(5));

        foreach (AVisitor vis in listVis)
            foreach (AElement elem in list)
            {
                vis.Visit(elem);
            }

        Console.ReadLine();
    }
}

Here is the result of sample execution:

 

In (1) we have a new invocation call. Even in compile-time, it is resolved to the method (2). Then during runtime, using Reflection, the type of argument is resolved in (3) and invocation passed to (4).

Limitation of this solution. As with any solution, this has some limitations/unwanted side effects:

  • There is a strong cyclic dependency between class hierarchies, Elements, and Visitors. That can be a problem if hierarchies need to be frequently updated.
  • Note that for Visitor to access data attribute Id of Element, that attribute needs to be public. That breaks the encapsulation principle a bit. For example, in our first solution, class diagram Picture1-1, method V1() could operate on private members of class Element.
  • Note that in (2), every class inherited from AElement is explicitly mentioned and checked for type. Missing some type might be a problem for implementation. One possible solution would be to discover all types in assembly by using Reflection and automatically dispatch to all classes that inherit from AElement. But, we are not going to do that here.

 

Visitor Pattern in C# – Version 5 – Generics Visitor

Generics Visitor version of Visitor Pattern is similar to Reflective Visitor pattern because it relies on 1) Reflection to dynamically discover type at runtime; 2) C# Generics to specify interfaces Visitor implements. We again have “Double dispatch” since we are dispatching to the concrete method based on types of two objects, just used language mechanism is different.

Here is what the class diagram looks like for sample code:

 

Here is the code of this:

 

public abstract class Element
{
    public abstract void Accept(IVisitor visitor);
}

public class ElementA : Element
{
	public int Id = 0;

	public ElementA(int Id)
	{
		this.Id = Id;
	}

	public override void Accept(IVisitor visitor)     //(2)
	{
		if (visitor is IVisitor<ElementA>)
		{
			((IVisitor<ElementA>)visitor).Visit(this);
		}
	}
}

public class ElementB : Element
{
	public int Id = 0;

	public ElementB(int Id)
	{
		this.Id = Id;
	}

	public override void Accept(IVisitor visitor)     //(2)
	{
		if (visitor is IVisitor<ElementB>)
		{
			((IVisitor<ElementB>)visitor).Visit(this);
		}
	}
}

public interface IVisitor { }; // marker interface

public interface IVisitor<TVisitable>
{
	void Visit(TVisitable obj);
}

public class Visitor1 : IVisitor,
			IVisitor<ElementA>, IVisitor<ElementB>
{
	public void Visit(ElementA ElemA)   //(3)
	{
		Console.WriteLine("{0} with Id={1} visited by {2}",
			ElemA.GetType().Name, ElemA.Id, this.GetType().Name);
	}

	public void Visit(ElementB ElemB)
	{
		Console.WriteLine("{0} with Id={1} visited by {2}",
			ElemB.GetType().Name, ElemB.Id, this.GetType().Name);
	}
}

public class Visitor2 : IVisitor,
			IVisitor<ElementA>, IVisitor<ElementB>
{
	public void Visit(ElementA ElemA)
	{
		Console.WriteLine("{0} with Id={1} visited by {2}",
			ElemA.GetType().Name, ElemA.Id, this.GetType().Name);
	}

	public void Visit(ElementB ElemB)
	{
		Console.WriteLine("{0} with Id={1} visited by {2}",
			ElemB.GetType().Name, ElemB.Id, this.GetType().Name);
	}
}

class Client
{
	static void Main(string[] args)
	{
		//--single element, base class call-----------------------
		Element element = new ElementA(1);
		IVisitor visitor = new Visitor1();

		element.Accept(visitor);  //(1)

		//--collection of elements-----------------
		List<IVisitor> listVis = new List<IVisitor>();
		listVis.Add(new Visitor1());
		listVis.Add(new Visitor2());

		List<Element> list = new List<Element>();
		list.Add(new ElementA(2));
		list.Add(new ElementB(3));
		list.Add(new ElementA(4));
		list.Add(new ElementB(5));

		foreach (IVisitor vis in listVis)
			foreach (Element elem in list)
			{
				elem.Accept(vis);
			}

		Console.ReadLine();
	}
}

Here is the result of sample execution:

 

In (1) we have a new invocation call. During runtime, it is dynamically bound to (2). Then in (2), we have to use Reflection to explicitly resolve it to (3). 

Limitation of this solution. As with any solution, this has some limitations/unwanted side effects:

  • Note that for Visitor to access data attribute Id of Element, that attribute needs to be public. That breaks the encapsulation principle a bit. For example, in our first solution, class diagram Picture1-1, method V1() could operate on private members of class Element.

 

Conclusion

First, we discussed our motivation and the problem we are trying to solve. It is important what we are trying to achieve since there might be more than one way to address the problem.

Then we showed “Classic Visitor”, which is a version proposed by GoF and often mentioned in literature. I think due to the limitation of languages (C++. Smalltalk) of the time it was created, that was proposed as the only and ultimate solution.

Modern OO languages, such as C#, have new features such as “dynamic object” and “Reflection” that enable us to achieve the same goal with different means. That is shown here in the other 4 versions of the Visitor Pattern. If you like, you can think of them as “modern-C#-enabled” alternative versions of Visitor Pattern.

 

No Comments

Post A Comment