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.