What is Runtime Polymorphism (Late Binding or Dynamic Binding)?
Short Answer:
Runtime polymorphism, also known as late binding or dynamic binding, is a feature in object-oriented programming where the method to be executed is determined at runtime based on the actual type of the object. This is achieved through method overriding and is a key concept in inheritance and subclassing.
Detailed Explanation:
What is Runtime Polymorphism?
Runtime polymorphism allows a program to decide which method to call at runtime, rather than at compile time. This is particularly useful when you have a base class and multiple derived classes, and you want to call a method that behaves differently depending on the actual object type.
How Does Runtime Polymorphism Work?
Runtime polymorphism is achieved using method overriding. Here’s how it works:
-
The base class defines a method with the
virtual
keyword, indicating that it can be overridden by derived classes.
-
The derived classes override this method using the
override
keyword, providing their own implementation.
-
At runtime, the program determines which version of the method to call based on the actual object type.
Why Do We Need Runtime Polymorphism?
Runtime polymorphism is useful in scenarios where:
- You want to write flexible and reusable code.
- You need to handle objects of different types in a uniform way.
- You want to extend functionality without modifying existing code (Open/Closed Principle).
Real-World Example: Employee Salary Calculation
Let’s look at a real-world example to understand runtime polymorphism better. Suppose we have a base class Employee
and two derived classes FullTimeEmployee
and PartTimeEmployee
. Each employee type has a different way of calculating their salary.
using System;
class Employee
{
public string Name { get; set; }
public Employee(string name)
{
Name = name;
}
// Virtual method to be overridden by derived classes
public virtual double CalculateSalary()
{
return 0; // Default implementation for generic employee
}
}
class FullTimeEmployee : Employee
{
public double MonthlySalary { get; set; }
public FullTimeEmployee(string name, double monthlySalary)
: base(name)
{
MonthlySalary = monthlySalary;
}
// Override the CalculateSalary method
public override double CalculateSalary()
{
return MonthlySalary;
}
}
class PartTimeEmployee : Employee
{
public double HourlyRate { get; set; }
public int HoursWorked { get; set; }
public PartTimeEmployee(string name, double hourlyRate, int hoursWorked)
: base(name)
{
HourlyRate = hourlyRate;
HoursWorked = hoursWorked;
}
// Override the CalculateSalary method
public override double CalculateSalary()
{
return HourlyRate * HoursWorked;
}
}
class Program
{
static void Main(string[] args)
{
// Create objects of derived classes
Employee fullTimeEmployee = new FullTimeEmployee("John Doe", 4000);
Employee partTimeEmployee = new PartTimeEmployee("Jane Smith", 20, 30);
// Call the CalculateSalary method
Console.WriteLine($"{fullTimeEmployee.Name}'s Salary: ${fullTimeEmployee.CalculateSalary()}");
Console.WriteLine($"{partTimeEmployee.Name}'s Salary: ${partTimeEmployee.CalculateSalary()}");
}
}
In this example:
- The
Employee
class has a virtual
method CalculateSalary()
.
- The
FullTimeEmployee
and PartTimeEmployee
classes override this method to provide their own salary calculation logic.
- At runtime, the program determines which version of
CalculateSalary()
to call based on the actual object type.
When you run the program, the output will be:
John Doe's Salary: $4000
Jane Smith's Salary: $600
Key Points to Remember
- Runtime polymorphism is achieved using
virtual
and override
keywords.
- The decision about which method to call is made at runtime, not at compile time.
- It promotes code reusability, flexibility, and extensibility.
Conclusion
Runtime polymorphism is a powerful feature of object-oriented programming that allows you to write flexible and reusable code. By using method overriding, you can define a common interface in a base class and provide specific implementations in derived classes. This makes your code more adaptable and easier to maintain, especially in scenarios where you need to handle multiple object types in a uniform way.