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 keyword virtual, 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.

Although 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 method MakeSound to behave according to the concrete type of the object at runtime.

Method hiding with new

In C#, there is another way to redefine the behavior of an inherited method using the keyword new. 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 reference of the base class type, 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 mimics sounds.");
    }
}

In this example, the class Parrot defines a new version of MakeSound using new, instead of override. This means that the method of Parrot is only present 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 which 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 through the use of interfaces. Interfaces define contracts that classes must implement, allowing you to work with different classes that implement the same interface in a uniform manner.

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 classes Dog and Cat implement the interface ISound, which means that both must define the method MakeSound. 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 simplifies the handling of 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.