C sharp - Multiple Dispatch using dynamic in C#

1. What is Multiple Dispatch?

In most object-oriented languages (including C# by default), method selection is based on the compile-time type of the reference.
This is called single dispatch.

Multiple dispatch means:
Method selection depends on the runtime types of multiple arguments, not just one.

C# does not natively support full multiple dispatch like some other languages, but it can simulate it using the dynamic keyword.


2. Single Dispatch in C#

Example:

class Animal { }
class Dog : Animal { }

class Program
{
    static void Speak(Animal a)
    {
        Console.WriteLine("Animal sound");
    }

    static void Speak(Dog d)
    {
        Console.WriteLine("Bark");
    }

    static void Main()
    {
        Animal a = new Dog();
        Speak(a);
    }
}

Output:

Animal sound

Why?
Because method overload resolution happens at compile time based on declared type (Animal), not runtime type (Dog).


3. Using dynamic for Runtime Dispatch

Now change:

static void Main()
{
    dynamic a = new Dog();
    Speak(a);
}

Output:

Bark

Now:

  • Method resolution happens at runtime

  • Actual object type (Dog) is used

This simulates multiple dispatch behavior.


4. What is dynamic?

dynamic tells the compiler:

Skip compile-time type checking for this variable.
Resolve everything at runtime.

Internally:

  • C# uses the Dynamic Language Runtime (DLR)

  • Method binding happens during execution


5. Multiple Dispatch with Two Parameters

Example:

class Shape { }
class Circle : Shape { }
class Rectangle : Shape { }

class Program
{
    static void Interact(Circle c, Rectangle r)
    {
        Console.WriteLine("Circle interacts with Rectangle");
    }

    static void Interact(Shape s1, Shape s2)
    {
        Console.WriteLine("Generic interaction");
    }

    static void Main()
    {
        dynamic c = new Circle();
        dynamic r = new Rectangle();

        Interact(c, r);
    }
}

Output:

Circle interacts with Rectangle

Here:

  • Both parameters are resolved at runtime

  • Closest matching overload is selected

This resembles true multiple dispatch.


6. When is dynamic Useful?

  1. Interoperability with dynamic languages (Python, JavaScript)

  2. COM interop

  3. Reflection simplification

  4. JSON handling

  5. Runtime plugin systems

Example:

dynamic obj = GetUnknownObject();
obj.SomeMethod();

No casting required.


7. Performance Consideration

Using dynamic:

  • Slower than static typing

  • Requires runtime binder

  • Cannot be inlined by JIT

  • Loses compile-time safety

Therefore:
Use only when necessary.


8. Differences: object vs dynamic

object dynamic
Checked at compile time Checked at runtime
Requires casting No casting required
Safe Less safe
Better performance Slower

Example:

object obj = "Hello";
obj.Length; // Compile error

With dynamic:

dynamic obj = "Hello";
Console.WriteLine(obj.Length); // Works

9. Risk of Runtime Errors

Since type checking is skipped:

dynamic x = 5;
x.NonExistentMethod();

This compiles successfully but crashes at runtime.

So debugging becomes harder.


10. Real-World Example: Visitor Pattern Alternative

Traditional Visitor Pattern is used to simulate multiple dispatch.

Using dynamic, you can simplify visitor logic without complex pattern implementation.

But:

  • It sacrifices type safety

  • Not always recommended for large systems