Language: EN

csharp-polimorfismo

What is and how to use polymorphism in C#

Polymorphism is one of the fundamental principles of object-oriented programming (OOP), which allows objects to be treated as instances of their base class while retaining their own specific behavior.

In C#, this is achieved through:

  • Virtual methods
  • Abstract methods
  • Interfaces

If you want to learn more about object-oriented programming, here is the link to the Object-Oriented Programming Course.

Virtual methods and overriding with override

A virtual method is one that is declared in a base class and can be overridden in a derived class to modify or extend its behavior.

In the base class, the method is marked with the virtual keyword, while in the derived class, override is used to specify that this method has been modified.

public class Animal
{
    public virtual void MakeSound()
    {
        Console.WriteLine("The animal makes a sound.");
    }
}

public class Dog : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("The dog barks.");
    }
}

public class Cat : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("The cat meows.");
    }
}

In the previous example, MakeSound is a virtual method in the base class Animal.

Both Dog and Cat override this method to specify a different sound, thus allowing each subclass to define its specific behavior.

Animal myAnimal = new Dog();
myAnimal.MakeSound(); // Output: The dog barks.

myAnimal = new Cat();
myAnimal.MakeSound(); // Output: The cat meows.

Even though the variable myAnimal is of type Animal, it executes the overridden method in the specific class (Dog or Cat).

This is polymorphism in action: it allows the MakeSound method to behave according to the concrete type of the object at runtime.

Hiding methods with new

In C#, there is another way to redefine the behavior of an inherited method using the new keyword. This approach is called method hiding and allows a derived class to define a new version of a method from the base class.

By using new instead of override, the method in the derived class hides the one in the base class. This means that when the method is called from a base class type reference, the original method is executed, not the one from the derived class.

public class Animal
{
    public virtual void MakeSound()
    {
        Console.WriteLine("The animal makes a sound.");
    }
}

public class Parrot : Animal
{
    public new void MakeSound()
    {
        Console.WriteLine("The parrot imitates sounds.");
    }
}

In this example, the Parrot class defines a new version of MakeSound using new, instead of override. This makes the Parrot method present only when the object is accessed directly as type Parrot.

The new keyword is useful when you need to define a method in a subclass with the same name as the one in the base class, but that should not modify its polymorphic behavior.


Although new is less common than override, it can be a good choice if you want to provide an alternative behavior without changing the structure of the base class hierarchy.

Polymorphism with interfaces

Another way to implement polymorphism in C# is by using interfaces. Interfaces define contracts that classes must implement, allowing you to work with different classes that implement the same interface in a uniform way.

public interface ISound
{
    void MakeSound();
}

public class Dog : ISound
{
    public void MakeSound()
    {
        Console.WriteLine("The dog barks.");
    }
}

public class Cat : ISound
{
    public void MakeSound()
    {
        Console.WriteLine("The cat meows.");
    }
}

In this case, the Dog and Cat classes implement the ISound interface, which means both must define the MakeSound method. This allows treating Dog and Cat polymorphically.

ISound mySound = new Dog();
mySound.MakeSound(); // Output: The dog barks.

mySound = new Cat();
mySound.MakeSound(); // Output: The cat meows.

Using interfaces makes it easier to handle different types of objects in a standardized and extensible manner. We can introduce new classes that implement ISound without altering the code that uses this interface, allowing for great flexibility.