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.