Let’s start with toy problem: you have a basket which contains fruits and you want to iterate over all the fruits and do something different with each type of fruit.

A trivial implementation looks something like this:

class Fruit
{
    public virtual Eat()
    {
        Console.WriteLine("Eating some unknown fruit");
    }
}

class Apple : Fruit
{
    public override Eat()
    {
        Console.WriteLine("Eating an apple");
    }
}

class Orange : Fruit
{
    public virtual Eat()
    {
        Console.WriteLine("Eating an orange");
    }
}

class Program
{
    static void Main(string[] args)
    {
        List<Fruit> basket = new List<Fruit>();
        basket.Add(new Apple());
        basket.Add(new Orange());

        basket.ForEach(f => f.Eat());
    }
}

This is great when you control the Fruit class hierarchy.

What can you do when the Fruit hierarchy is out of your control? And it has no Eat method?

You could try overloads

class Program
{
    static void Main(string[] args)
    {
        List<Fruit> basket = new List<Fruit>();
        basket.Add(new Apple());
        basket.Add(new Orange());

        basket.ForEach(Eat);
    }
    
    static void Eat(Fruit fruit)
    {
        Console.WriteLine("Eating some unknown fruit");
    }
    
    static void Eat(Apple apple)
    {
        Console.WriteLine("Eating an apple");
    }
    
    static void Eat(Orange orange)
    {
        Console.WriteLine("Eating an orange");
    }
}

unfortunately it will just call the Fruit kind of overload:

Eating some unknown fruit
Eating some unknown fruit

dynamic to the rescue:

basket.ForEach(fruit => Eat((dynamic)fruit);

results in

Eating an apple
Eating an orange

What is going on here?