Thursday, 31 May 2018

Double Dispatch in C#

First few tests to understand the basics:

What do you think about the GetEmployeeType() method that will be called - will it be on Employee class or Manager class?

void Main()
{
 Employee e = new Manager();
 Console.WriteLine(e.GetEmployeeType()); 
}

class Employee
{
 public virtual string GetEmployeeType() {
  return nameof(Employee);
 }
}

class Manager : Employee
{
 public override string GetEmployeeType() {
  return nameof(Manager);
 }
}


Output: Manager

Since Employee object 'e' points to Manager, it will call the GetEmployeeType of Manager class.

Another one:

void Main()
{
 Employee employee = new Manager();
 Console.WriteLine(CalculateSalary(employee));
}

string CalculateSalary(Employee e)
{
 return $"{nameof(CalculateSalary)} of {nameof(Employee)}";
}

string CalculateSalary(Manager e)
{
 return $"{nameof(CalculateSalary)} of {nameof(Manager)}";
}

class Employee ...

class Manager : Employee ...


Output: CalculateSalary of Employee

Since the object is of type Employee the CalculateSalary accepting Employee will be called.


Taking this further, let's move the CalculateSalary to a class.

void Main()
{
 Employee employee = new Employee();
 Employee manager = new Manager();

 SalaryAlgo algo = new SalaryAlgoBasedOnPerformance();
 Console.WriteLine(algo.CalculateSalary(employee));
 Console.WriteLine(algo.CalculateSalary(manager));
}

class SalaryAlgo
{
 public virtual string CalculateSalary(Employee e)
 {
  return $"{nameof(CalculateSalary)} of {nameof(Employee)} based on {nameof(SalaryAlgo)}";
 }

 public virtual string CalculateSalary(Manager e)
 {
  return $"{nameof(CalculateSalary)} of {nameof(Manager)} based on {nameof(SalaryAlgo)}";
 }
}

class SalaryAlgoBasedOnPerformance : SalaryAlgo
{
 public override string CalculateSalary(Employee e)
 {
  return $"{nameof(CalculateSalary)} of {nameof(Employee)} based on {nameof(SalaryAlgoBasedOnPerformance)}";
 }

 public override string CalculateSalary(Manager e)
 {
  return $"{nameof(CalculateSalary)} of {nameof(Manager)} based on {nameof(SalaryAlgoBasedOnPerformance)}";
 }
}

class Employee ...

class Manager : Employee ...

Output:

CalculateSalary of Employee based on SalaryAlgoBasedOnPerformance
CalculateSalary of Employee based on SalaryAlgoBasedOnPerformance

As you can see, CalculateSalary(Manager) is not at all called. This is because the method to call is decided at compile time and the declared type is used to identify the method not the runtime type.

A quick way to fix this is to use "dynamic" keyword:

 Console.WriteLine(algo.CalculateSalary((dynamic)manager));

Output:

CalculateSalary of Employee based on SalaryAlgoBasedOnPerformance
CalculateSalary of Manager based on SalaryAlgoBasedOnPerformance

Another way to resolve this is to use Visitor pattern.

Helpful links:

https://blogs.msdn.microsoft.com/curth/2008/11/15/c-dynamic-and-multiple-dispatch/

EDIT: Post that implements Visitor pattern to resolve this problem.

No comments:

Post a Comment

Note: only a member of this blog may post a comment.

Shorts - week 3, 2022

Post with links to what I am reading: 1. A very good post on different aspects of system architecture: https://lethain.com/introduction-to-a...