EDITOR'S NOTE: Hi y'all! Please welcome Huy Lương Vạn to the guest writer program! He is a first-time blogger and his article is below. Be nice! Comments are open at the end of the post. -Matthew

Have you ever heard about the Visitor Pattern – one of the Gang of Four Patterns? Have you tried researching it on the internet but still don’t know which problems it’s solving and which use cases should be applied in the real world. In this tutorial, I will show you a different approach called “Double Dispatch” to understand the Visitor pattern easier and how it is used in the .NET Library.

The Problem

When searching on the internet, I usually see this common definition:

“The Visitor pattern lets you define a new operation to a collection of objects without changing the objects themselves.”

I would like to say that it’s hard for me to understand this definition at first time reading it. Almost patterns help you adding new operation without modifying the existing structure or following “Open for extensions but Closed for modification” principle in another word. Therefore, I would like to introduce a new approach to understand this pattern more simple and clearer.

“The Visitor pattern lets you to implement Double Dispatch in programming language.”

First, we need to know what is “Method Dispatch”. Method Dispatch is how a program selects which instructions to execute when invoking a method. In case we have a lot of methods having the same name how we know which method will be called. This is when the “Single Dispatch” comes to play.

Single Dispatch is a method dispatcher which decides which methods to be called based on the runtime type of the sender(instance) and the compile-type of the method arguments.

Single Dispatch is a basic language feature that almost programming languages support it like C#, Java, C++ etc... Double Dispatch is more advanced compared to Single Dispatch.

Double Dispatch is a method dispatcher which decides which methods to be called based on the runtime type of the sender(instance) and the runtime type of the method arguments.

None of programming languages support this feature but we can implement it using Visitor pattern, let’s consider the following example:

public class Package { }
public class AdvancedPackage : Package { }
public class StandardPackage : Package { }
public class Customer
{
    public virtual void Buy(Package package)
    {
        Console.WriteLine("Customer is buying StandardPackage.");
    }
    public virtual void Buy(StandardPackage package)
    {
        Console.WriteLine("Customer is buying StandardPackage.");
    }
    public virtual void Buy(AdvancedPackage package)
    {
        Console.WriteLine("Customer is buying StandardPackage.");
    }
}

public class IndividualCustomer : Customer
{
    public override void Buy(Package package)
    {
        Console.WriteLine("IndividualCustomer is buying Package.");
    }
    public override void Buy(StandardPackage package)
    {
        Console.WriteLine("IndividualCustomer is buying StandardPackage.");
    }
    public override void Buy(AdvancedPackage package)
    {
        Console.WriteLine("IndividualCustomer is buying AdvancedPackage.");
    }
}

public class EnterpriseCustomer : Customer
{
    public override void Buy(Package package)
    {
        Console.WriteLine("EnterpriseCustomer is buying Package.");
    }
    public override void Buy(StandardPackage package)
    {
        Console.WriteLine("EnterpriseCustomer is buying StandaradPackage.");
    }
    public override void Buy(AdvancedPackage package)
    {
        Console.WriteLine("EnterpriseCustomer is buying AdvancedPackage.");
    }
}

public static void Main(string[] args)
{
    Customer individualCustomer = new IndividualCustomer();
    StandardPackage standardPackage = new StandardPackage();
    individualCustomer.Buy(standardPackage); // (1)
    Package package = new StandardPackage();
    individualCustomer.Buy(package); // (2)
    return;
}

The line (1) will output: “IndividualCustomer is buying StandardPackage”.

However, the line (2) will output: “IndividualCustomer is buying Package”. The reason for this output is that package variable has the Package type so Buy(Package) method is called (following as Single Dispatch), that is not our expectation. To solve this problem, we can use the is keyword in C# to check the type of current instance.

public class IndividualCustomer : Customer
{
    public override void Buy(Package package)
    {
        if (package is StandardPackage standardPackage)
        {
            Buy(standardPackage);
            return;
        }
        else if (package is AdvancedPackage advancedPackage)
        {
            Buy(advancedPackage);
            return;
        }
        Console.WriteLine($"IndividualCustomer is buying Package.");
    }

    public override void Buy(StandardPackage package)
    {
        Console.WriteLine("IndividualCustomer is buying StandardPack-age.");
    }

    public override void Buy(AdvancedPackage package)
    {
        Console.WriteLine("IndividualCustomer is buying AdvancedPack-age.");
    }
}

The problem with this solution is that if we are going to have more classes derived from Package type then we have to add a lot of methods to Customer class and its derived classes.

The Solution

Visitor pattern is used to solve this such issue. This pattern has the following structure:

- Element: is the type of argument. For previous example, it will be Package, StandardPackage.

- IVisitor interface: contains all the methods which has the Element as parameters.

- ConcreteVisitors: implement IVisitor interface which contain all the logic for specific Element. We should have one ConcreteVisitor for each overload method.

public interface IVisitor
{
    void Visit(Package package);
    void Visit(StandardPackage package);
    void Visit(AdvancedPackage package);
}

 public class IndividualCustomerBuyVisitor : IVisitor
 {
    public void Visit(Package package)
    {
        Console.WriteLine("IndividualCustomer is buying Package.");
    }
    public void Visit(StandardPackage package)
    {
        Console.WriteLine("IndividualCustomer is buying StandardPackage.");
    }
    public void Visit(AdvancedPackage package)
    {
        Console.WriteLine("IndividualCustomer is buying AdvancedPackage.");
    }
}

public class EnterpriseCustomerBuyVisitor : IVisitor
{
    public void Visit(Package package)
    {
        Console.WriteLine("EnterpriseCustomer is buying Package.");
    }
    public void Visit(StandardPackage package)
    {
        Console.WriteLine("EnterpriseCustomer is buying StandardPackage.");
    }
    public void Visit(AdvancedPackage package)
    {
        Console.WriteLine("EnterpriseCustomer is buying AdvancedPackage.");
    }
}

public class Package
{
    public virtual void Accept(IVisitor visitor) => visitor.Visit(this);
}

public class AdvancedPackage : Package 
{
    public override void Accept(IVisitor visitor) => visitor.Visit(this);
}

public class StandardPackage : Package
{
    public override void Accept(IVisitor visitor) => visitor.Visit(this);
}

public static void Main(string[] args)
{
    Package package = new AdvancedPackage();

    IVisitor visitor = new IndividualCustomerBuyVisitor();
    package.Accept(visitor);// IndividualCustomer is buying Advanced-Package.

    visitor = new EnterpriseCustomerBuyVisitor();
    package.Accept(visitor);// EnterpriseCustomer is buying AdvancedPackage.   
}

Next, we will cover how this pattern is used in System.Linq – a common .NET Library. Let’s take a look at the Expression class, it provides the base class from which the classes that represent expression tree nodes are derived. Some common classes derived from Expression class are:

- ConditionalExpression: Represents an expression that has a conditional operator.

- MemberExpression: Represents accessing a field or property.

- BinaryExpression: Represents an expression that has a binary operator.

- ConstantExpression: Represents an expression that has a constant value.

Following the Visitor Pattern, .NET Library introduces an ExpressionVisitor – represents a visitor which is designed to be inherited to traversing, rewriting, executing an expression tree. For example, if we have the following LINQ query:

public class Person
{
    public int Id { get; set; }

    public string FirstName { get; set; }

    public string LastName { get; set; }
}

public static void Main(string[] args)
{
    var list = new List<Person>()
    {
        new Person() { }
    };

    var query = list.AsQueryable()
        .Where(n => n.Id == 5)
        .Select(n => new { n.Id, n.FirstName });

    return;
}

The generated expression tree will be like this:

Next, thank to Visitor Pattern we can derive the ExpressionVisitor class to traverse this expression tree and do whatever we want. For example, I will create a GenerateSqlVisitor class to generate a simple SQL query based on this expression tree. The general idea is that we will visit each expression and extract information to build SQL query.

public static class QueryableExtensions
{
    public static string ToSql<T>(this IQueryable<T> query)
    {
        var visitor = new GenerateSqlVisitor();
        visitor.Visit(query.Expression);
        return visitor.GenerateSql();
    }
}

public class GenerateSqlVisitor : ExpressionVisitor
{
    private string filterPropertyName;
    private string filterPropertyValue;
    private string filterOperator;
    private List<string> selectFields = new();
    private string tableName;

    protected override Expression VisitConstant(ConstantExpression node)
    {
        if (node.Type.IsConstructedGenericType)
        {
            tableName = node.Type.GenericTypeArguments[0].Name;
        }
        return base.VisitConstant(node);
    }
    protected override Expression VisitLambda<T>(Expression<T> node)
    {
        if (node.Body is BinaryExpression binaryExpression)
        {
            if (binaryExpression.Left is MemberExpression memberExpression)
            {
                filterPropertyName = memberExpression.Member.Name;
            }
            if (binaryExpression.Right is ConstantExpression constantExp)
            {
                filterPropertyValue = constantExp.Value.ToString();
            }
            switch (binaryExpression.NodeType)
            {
                case ExpressionType.Equal:
                    filterOperator = "=";
                    break;
                case ExpressionType.NotEqual:
                    filterOperator = "!=";
                    break;
                case ExpressionType.GreaterThan:
                    filterOperator = ">";
                    break;
                case ExpressionType.LessThan:
                    filterOperator = "<";
                    break;
                default:
                    throw new NotSupportedException();
            }
        }
        else if (node.Body is NewExpression newExpression)
        {
            selectFields.AddRange(newExpression.Members.Select(m => m.Name));
        }
        return base.VisitLambda(node);
    }
    public string GenerateSql()
    {
        return @$"SELECT {string.Join(",", selectFields)} 
            FROM {tableName} 
            WHERE {filterPropertyName} {filterOperator} {filterPropertyValue}";
    }
}

public static void Main(string[] args)
{
    var list = new List<Person>()
    {
        new Person() { }
    };

    var query = list.AsQueryable()
        .Where(n => n.Id == 5)
        .Select(n => new { n.Id, n.FirstName });

    Console.WriteLine(query.ToSql());

    return;
}

Then the output from console will be:
SELECT Id,FirstName
FROM Person
WHERE Id = 5

Summary

The Visitor Pattern helps us to solve problems when we want to invoke a method based on the runtime type of instance and runtime type of method arguments. From my experience, this pattern used intensively when writing a language compiler but rarely used for other cases. You can experience more with this pattern by looking into /contributing to .NET EF Core library, it applies this pattern intensively to build the SQL query.