In the last post (please check that out before reading this, as it shows the problem we are trying to solve here) we saw how C# uses single-dispatch to select the methods to call. The argument type is determined at compile time and that is used to select the method out of many.
Here is an implementation of visitor pattern to resolve that problem:
void Main() { Employee employee = new Employee(); Employee manager = new Manager(); IVisitor visitor = new SalaryAlgoPerformanceVisitor(); employee.Accept(visitor); manager.Accept(visitor); Console.WriteLine(visitor.Output); } interface IVisitor { string Output {get;} void Visit(Employee e); void Visit(Manager e); } class SalaryAlgoPerformanceVisitor : IVisitor { public string Output { get; private set;} public void Visit(Employee e) { Output += $"Calculating salary of {nameof(Employee)} based on {nameof(SalaryAlgoPerformanceVisitor)}\n"; } public void Visit(Manager e) { Output += $"Calculating salary of {nameof(Manager)} based on {nameof(SalaryAlgoPerformanceVisitor)}\n"; } } class Employee { public virtual string GetEmployeeType() { return nameof(Employee); } public virtual void Accept(IVisitor visitor) { visitor.Visit(this); } } class Manager : Employee { public override string GetEmployeeType() { return nameof(Manager); } public override void Accept(IVisitor visitor) { visitor.Visit(this); } }
Output:
Calculating salary of Employee based on SalaryAlgoPerformanceVisitor Calculating salary of Manager based on SalaryAlgoPerformanceVisitor
You will notice that Accept method is resolved using the run time type of Employee object - this is the first dispatch. Then in the Accept method we use (this) so that appropriate method is called on the visitor - this is the second dispatch.
Since Accept is inside the class, we use the type of (this) to call the visitor.