Meta Description: Learn about polymorphism in C#, a key object-oriented programming concept. Understand how to use virtual and override keywords to achieve flexible and maintainable code with real-world examples.
In C#, polymorphism is a key concept of object-oriented programming that allows methods to have multiple forms. If you have understood inheritance, polymorphism is the next essential step in leveraging the power of object-oriented programming to make your code more dynamic and maintainable. Let's break down polymorphism in a clear, practical way.
The Concept of Polymorphism
The term polymorphism comes from Greek, meaning "many forms." In programming, polymorphism allows objects to take on different forms depending on their context. Specifically, polymorphism allows a base class to define methods that derived classes can override to provide their own unique implementations.
Polymorphism helps us:
- Reuse code effectively.
- Write more extensible and maintainable code.
- Treat instances of derived classes uniformly through a base class reference.
Consider a scenario involving employees in a company. You might have a base class Employee
, and derived classes like Manager
and Intern
. Although every employee has some common functionalities, such as working, each type of employee may have different ways of implementing that functionality.
Virtual and Override Keywords
To achieve polymorphism in C#, we use the virtual
and override
keywords. Let’s see an example:
public class Employee
{
public virtual void PerformWork()
{
Console.WriteLine("Employee is working.");
}
}
The PerformWork
method here is marked as virtual
, meaning it can be overridden in any derived class.
Now let’s define a Manager
class:
public class Manager : Employee
{
public override void PerformWork()
{
Console.WriteLine("Manager is managing team meetings and setting goals.");
}
}
And an Intern
class:
public class Intern : Employee
{
public override void PerformWork()
{
Console.WriteLine("Intern is assisting with tasks and learning the workflow.");
}
}
By marking PerformWork
in Employee
as virtual
, we have given derived classes like Manager
and Intern
the ability to provide their own specific implementation of that method using the override
keyword.
Polymorphism in Action
Here’s a more realistic scenario where polymorphism is useful: we have a list of various employees, and we want to call the PerformWork
method on all of them without having to worry about their specific type.
List<Employee> employees = new List<Employee>
{
new Employee(),
new Manager(),
new Intern()
};
foreach (var employee in employees)
{
employee.PerformWork();
}
Output:
Employee is working.
Manager is managing team meetings and setting goals.
Intern is assisting with tasks and learning the workflow.
Notice how each employee type calls the appropriate PerformWork
method based on the actual instance. Even though the list is typed as Employee
, each derived class’s overridden method is used. This is the essence of polymorphism.
Why Use Polymorphism?
One common question is, "Why would I use polymorphism?" One reason is simplicity. When you have a collection of various objects that share a common base class, polymorphism allows you to call methods uniformly, without writing specific logic for each type. This makes your code more flexible and scalable.
Imagine we want to process monthly salaries for different employees:
public class Employee
{
public virtual void ProcessSalary()
{
Console.WriteLine("Processing standard salary.");
}
}
public class Manager : Employee
{
public override void ProcessSalary()
{
Console.WriteLine("Processing manager salary with bonuses.");
}
}
public class Intern : Employee
{
public override void ProcessSalary()
{
Console.WriteLine("Processing intern stipend.");
}
}
List<Employee> employees = new List<Employee>
{
new Employee(),
new Manager(),
new Intern()
};
foreach (var employee in employees)
{
employee.ProcessSalary();
}
Output:
Processing standard salary.
Processing manager salary with bonuses.
Processing intern stipend.
Using polymorphism, the correct ProcessSalary
method is called based on the type of the employee without additional if-else
or switch
statements, simplifying code management and reducing potential errors.
Base Type References to Derived Instances
Polymorphism also allows you to reference derived class instances using a base class reference. For example:
Employee manager = new Manager();
manager.PerformWork(); // Output: Manager is managing team meetings and setting goals.
Even though manager
is of type Employee
, it points to a Manager
object. When calling PerformWork
, the Manager
version of the method is invoked, thanks to polymorphism.
However, note that if the Manager
class has methods that do not exist in Employee
, those methods won’t be accessible through an Employee
reference:
public class Manager : Employee
{
public override void PerformWork()
{
Console.WriteLine("Manager is managing team meetings and setting goals.");
}
public void ConductMeeting()
{
Console.WriteLine("Manager is conducting a meeting.");
}
}
Employee employee = new Manager();
employee.ConductMeeting(); // Error: 'Employee' does not contain a definition for 'ConductMeeting'
Assignments to Practice Polymorphism
To help you understand polymorphism more deeply, here are three levels of assignments:
Easy Level
-
Create a Simple Inheritance Chain:
- Create a base class named
Vehicle
with avirtual
methodStartEngine()
. - Create derived classes
Car
andMotorbike
that override theStartEngine()
method. - Instantiate both
Car
andMotorbike
and callStartEngine()
.
- Create a base class named
Medium Level
-
Implement a Polymorphic Animal Hierarchy:
- Create an abstract base class
Animal
with a methodMakeSound()
. - Create derived classes like
Dog
,Cat
, andBird
that overrideMakeSound()
with different outputs. - Put the animals in a list of
Animal
and callMakeSound()
for each.
- Create an abstract base class
-
Bonus Calculation System:
- Create an
Employee
class with aGiveBonus()
method. - Create subclasses like
Manager
,SalesPerson
, andIntern
that overrideGiveBonus()
with their unique implementations. - Create a list of different types of employees and call
GiveBonus()
on each.
- Create an
Difficult Level
-
Shape Hierarchy with Abstract Classes:
- Create an abstract base class
Shape
with an abstract methodCalculateArea()
. - Implement derived classes like
Circle
,Rectangle
, andTriangle
with specific implementations ofCalculateArea()
. - Create a list of
Shape
objects and iterate over the list to calculate and print each shape's area.
- Create an abstract base class
Real-World Example: Company Hierarchy
To simulate a more complex real-world scenario, you can create a system to manage a company's hierarchy:
-
Base Class:
Employee
: Include common methods likePerformWork()
,TakeLeave()
. -
Derived Classes:
Manager
,Engineer
,Intern
: Each class should overridePerformWork()
and include unique methods likeApproveLeave()
(forManager
). -
Scenario:
- Create a list of
Employee
objects, containing different types of employees. - Iterate through this list, calling
PerformWork()
. - Implement methods like
AssignTask()
where managers can assign tasks to engineers, highlighting interaction between classes.
- Create a list of
Conclusion
Polymorphism allows you to write flexible, reusable, and maintainable code. It lets you treat objects of different types in a uniform way while still utilizing their specific behavior. With the right use of virtual
and override
keywords, polymorphism brings flexibility and scalability to your applications. It allows your code to be more readable and concise, eliminating redundancy and the need for cumbersome conditional statements.
Top comments (0)