Polymorphism means that a operation can also be applied to values of some other types.
There are multiple types of Polymorphism:
function overloading. The target is that a Method can be used with
different types without the need of being generic.The target of Ad hoc polymorphism is to create a method, that can be called by different datatypes without a need of type-conversion in the function call or generics. The following method(s) sumInt(par1, par2) can be called with different datatypes and has for each combination of types a own implementation:
public static int sumInt( int a, int b)
{
    return a + b;    
}
public static int sumInt( string a, string b)
{
    int _a, _b;
    
    if(!Int32.TryParse(a, out _a))
        _a = 0;
    
    if(!Int32.TryParse(b, out _b))
        _b = 0;
    
    return _a + _b;
}
public static int sumInt(string a, int b)
{
    int _a;
    
    if(!Int32.TryParse(a, out _a))
        _a = 0;    
    
    return _a + b;
}
public static int sumInt(int a, string b)
{        
    return sumInt(b,a);
}
Here's a example call:
public static void Main()
{
    Console.WriteLine(sumInt( 1 , 2 ));  //  3
    Console.WriteLine(sumInt("3","4"));  //  7
    Console.WriteLine(sumInt("5", 6 ));  // 11
    Console.WriteLine(sumInt( 7 ,"8"));  // 15
}
Subtyping is the use of inherit from a base class to generalize a similar behavior:
public interface Car{
    void refuel();
}
public class NormalCar : Car
{
    public void refuel()
    {
        Console.WriteLine("Refueling with petrol");    
    }
}
public class ElectricCar : Car
{
    public void refuel()
    {
        Console.WriteLine("Charging battery");    
    }
}
Both classes NormalCar and ElectricCar now have a method to refuel, but their own implementation. Here's a Example:
public static void Main()
{
    List<Car> cars = new List<Car>(){
        new NormalCar(),
        new ElectricCar()
    };
    
    cars.ForEach(x => x.refuel());
}
The output will be was following:
Refueling with petrol
Charging battery