Introduction
When you write a statement of a method call, it gets called at runtime. Now the question is how it gets called. It is invoked because the compiler has the mechanism which selects the most appropriate method and then it invokes it by passing arguments. That mechanism is commonly known as dispatching. So, dispatching is the process of transferring calls, i.e., dispatch a call to a function, or dispatch an event to a handler, etc.
Dispatch is the process of invoking or calling the method which can handle the task or action. Sometimes, there are multiple method lists available, so at that time, the best applicable method must be selected. This whole process of selecting the appropriate method is known as dispatching and the mechanism is generally known as dispatcher.
Every programming language needs some dispatching mechanism to select a method to invoke. This mechanism decides which method will be invoked at any particular moment of call.
Programming languages can be broadly divided into following types based on dispatching mechanism.
- Static dispatch
- Dynamic dispatch
Static Dispatch
When the method to invoke is decided at compile time, then this is known as Single Dispatch. The method’s signature is used to decide the method to call.
public class Survey
{
public void DoSurvey()
{
Console.WriteLine("DoSurvey is called");
}
}
Survey survey = new Survey();
survey.DoSurvey(); ------------------------- (1)
In the above example, when we write survey.DoSurvey() statement, DoSurvery method of Survey class will be called. So this is decided at compile time. As it is the decision of method at compile time, so it is static and hence known as Static Dispatch.
All programming languages supports Static Dispatch and have the best performance graph in comparison with Dynamic Dispatch mechanism.
Dynamic Dispatch
In OOPs, most often, there are times when method call can’t be decided until runtime. In polymorphic operation, it cannot be decided at compile time that which type’s method will be called, because it is the actual instance which will decide the method to be invoked. Such scenario can be handled by dynamic dispatch. In dynamic dispatch, the method is determined dynamically on the basis of actual instance type of the object.
Dynamic Dispatch is a kind of runtime overloading.
Dynamically dispatched languages could be divided in two groups:
- Single and
- Multiple dispatched
Single Disptach
Virtual method call is the Single Dispatching. In Virtual method Call, the action method is decided on the basis of actual instance type of an object, i.e., First of all, the actual type or runtime type of the instance is identified dynamically and then the method of that type is called. So, the whole process of selecting the method is dynamic, it can’t be identified at compile time.
Example 1
public class SurveyBase
{
public virtual void DoSurvey()
{
Console.WriteLine("Base Class");
}
}
public class Survey : SurveyBase
{
public override void DoSurvey()
{
Console.WriteLine("Derived Class");
}
}
SurveyBase base = new Survey();
base.DoSurvey();
In the above example, the method of derived class is determined at run time by the actual instance type which is held by the variable of type base.
Example 2
Let's see some examples:
public interface IAnimal { }
public class Mammals : IAnimal { }
public class Dog : Mammals { }
We have created an Animal hierarchy class via inheritance. Now, we create a Survey class, which has the method overload with a different parameter type.
public class Survey
{
public void DoSurvey(IAnimal animal)
{
Console.WriteLine("IAnimal Type");
}
public void DoSurvey(Mammals animal)
{
Console.WriteLine("Mammals Type");
}
public void DoSurvey(Dog animal)
{
Console.WriteLine("Dog Type");
}
}
Dog dog = new Dog();
Mammals mamels = new Mammals();
Survey survey = new Survey();
Console.WriteLine("survey.DoSurvey(dog) -> {0}", survey.DoSurvey(dog));
Console.WriteLine("survey.DoSurvey(mamels) -> {0}", survey.DoSurvey(mamels));
When we run the above example, we get the below output.
Figure 1
The above output shows method call is selected on the basis of parameter type which is done at run time.
Both examples, Example 1 and Example 2 demonstrate Single Dispatch.
Double/Multiple Dispatch
As we have seen in the above example, the method is determined on the basis of instance type or instance of parameter type, in short only one filtering criteria is used to select the appropriate method to invoke. What happens when we have two or more filtering criteria, i.e., method must be selected on the basis of runtime types of two or more objects involved while invoke is initiated. I know the previous line is confusing, actually it is a little bit tricky.
<u1:p>In double dispatch, both the object's type and the method argument's type is taken into account. It is like method overload resolution, the only difference is that the argument type is determined at runtime in double-dispatch instead of statically at compile-time. In multiple-dispatch, a method can have multiple arguments passed to it and then which method is invoked depends on each argument's type.
Consider the following example...
IAnimal animal = new Dog();
Mammals dogMamels = new Dog();
Console.WriteLine("survey.DoSurvey(animal) -> {0}", survey.DoSurvey(animal));
Console.WriteLine("survey.DoSurvey(dogMamels) -> {0}", survey.DoSurvey(dogMamels));
In the above example. a variable of type IAnimal is referring the object of type Dog, i.e., the object held by animal is of type Dog. In the same way, dogMamels of type Mammals is referring to the object of type Dog. Now, when we run the above code, the following result will be displayed on the console.
Figure 2
I think most of us will not expect the result. We might be expecting the result as of Figure 1. This is because the dispatcher is not considering the actual type held by the animal or dogMamels while selecting the method to invoke. Instead of identifying the actual type at runtime, it is considering the type which was at design time, i.e., the type of animal and dogMamels object is calculated at compile time, not at run time. This is because in most object oriented programming languages, the dispatcher calculates the actual type of only one object, i.e., the calling object's type as shown above in the section of Single Dispatch Example 1. Here, the parameter type is varying in inheritance hierarchy, so their actual type must be identified at run time.
The below example will extend the issue as defined above. The above issue will become even worse if we use List<IAnimal> to hold multiple types of other objects and try to execute some business logic on them.
Survey survey = new Survey();
List<IAnimal> animals = new List<IAnimal> {
new Dog(),
new Mammals(),
new Dog(),
new Mammals()
};
animals.ForEach(x => {
Console.WriteLine(survey.DoSurvey(x));
});
The above issue is with most of today’s object oriented programming languages like .NET and Java. They support only Single Dispatch, they do not support double or Multiple Dispatch.
Solution to Solve Double/Multiple Dispatch
There are two easy steps to solve this problem:
- Using Dynamic
- Visitor Pattern
Using Dynamic
Before C# 4.0, C# did not support double/multiple dispatch. In 4.0, dynamic keyword was introduced to simplify the COM interaction. This makes the CLR from statically typed language to dynamic language and enables C# to support multiple dispatch.
We have used dynamic keyword in the above example and this is a simple change which solved dynamic dispatch problem.
dynamic animal = new Dog();
dynamic dogMamels = new Dog();
Console.WriteLine("survey.DoSurvey(animal) -> {0}", survey.DoSurvey(animal));
Console.WriteLine("survey.DoSurvey(dogMamels) -> {0}", survey.DoSurvey(dogMamels));
After running the above code, the following result will get displayed on the console.
As shown above, we can use dynamic to solve the double/multiple dispatch issue, but we should not use it blindly. Actually, it was designed for some other purpose to simplify the COM communication, so, the impact must be considered as it can hurt the performance of our application because it will increase the size of our application working set.
Using Visitor Design Pattern
Visitor is the Design pattern which is used to solve the double dispatch problem. It's a separate topic to illustrate. So, to keep this article small and concise, I am not going to talk about visitor pattern in this article. It is a way of achieving double dispatch in an object oriented way.
If you want to explore this, I have another article on Visitor Pattern.
Points of Interest
Static Dispatch - Method to invoke is decided at compile time.
Single Dispatch - Virtual method call is the Single Dispatching. In Virtual method Call, the action method is decided on the basis of actual instance type of an object
Multiple Dispatch- It is about to invoke an overloaded method where the parameters vary among an inheritance hierarchy.
Double dispatch is a type of multiple dispatch. C# does support only Static and Single Dispatch, and with the help of dynamic keyword, it supports double/multiple dispatch.
Generally speaking, if your program needs double/multiple dispatch, then there might be chances of some application design issues. It will lend to the maintenance and extensible issues. Sometimes, it also creates unnecessary confusion to the developers. But anyhow, if you come across these issues, then we should avoid using Dynamic, as it has its own issues, so, we should find some object oriented way to resolve these issues and the Visitor pattern is the best suited pattern in these scenarios.
Keep coding...