Polymorphism is one of the core concepts of object-oriented programming (OOP) in C#. It allows methods to do different things based on the object that it is acting upon, even if they share the same name. In C#, polymorphism can be achieved through two main mechanisms: method overriding (runtime polymorphism) and method overloading (compile-time polymorphism).
1. Method Overriding (Runtime Polymorphism)
Method overriding allows a derived class to provide a specific implementation of a method that is already defined in its base class. This is achieved using the virtual
keyword in the base class and the override
keyword in the derived class.
Example:
csharp1using System; 2 3class Animal // Base class 4{ 5 public virtual void Speak() // Virtual method 6 { 7 Console.WriteLine("Animal speaks"); 8 } 9} 10 11class Dog : Animal // Derived class 12{ 13 public override void Speak() // Overriding the base class method 14 { 15 Console.WriteLine("Dog barks"); 16 } 17} 18 19class Cat : Animal // Another derived class 20{ 21 public override void Speak() // Overriding the base class method 22 { 23 Console.WriteLine("Cat meows"); 24 } 25} 26 27class Program 28{ 29 static void Main(string[] args) 30 { 31 Animal myAnimal; 32 33 myAnimal = new Dog(); // Upcasting 34 myAnimal.Speak(); // Output: Dog barks 35 36 myAnimal = new Cat(); // Upcasting 37 myAnimal.Speak(); // Output: Cat meows 38 } 39}
Explanation:
- The
Animal
class has a virtual methodSpeak()
. - The
Dog
andCat
classes override theSpeak()
method to provide their specific implementations. - In the
Main
method, we create anAnimal
reference and assign it to aDog
andCat
object. When we callSpeak()
, the correct method is invoked based on the actual object type, demonstrating runtime polymorphism.
2. Method Overloading (Compile-Time Polymorphism)
Method overloading occurs when multiple methods in the same class have the same name but different parameters (different type, number, or both). The appropriate method is determined at compile time based on the method signature.
Example:
csharp1using System; 2 3class MathOperations 4{ 5 // Method to add two integers 6 public int Add(int a, int b) 7 { 8 return a + b; 9 } 10 11 // Method to add three integers 12 public int Add(int a, int b, int c) 13 { 14 return a + b + c; 15 } 16 17 // Method to add two double values 18 public double Add(double a, double b) 19 { 20 return a + b; 21 } 22} 23 24class Program 25{ 26 static void Main(string[] args) 27 { 28 MathOperations math = new MathOperations(); 29 30 Console.WriteLine(math.Add(5, 10)); // Output: 15 31 Console.WriteLine(math.Add(5, 10, 15)); // Output: 30 32 Console.WriteLine(math.Add(5.5, 10.5)); // Output: 16 33 } 34}
Explanation:
- The
MathOperations
class has three overloadedAdd
methods, each with different parameters. - Depending on the arguments passed, the appropriate
Add
method is called, demonstrating compile-time polymorphism.
3. Polymorphism with Interfaces
Polymorphism can also be achieved through interfaces, where different classes implement the same interface.
Example:
csharp1using System; 2 3interface IShape 4{ 5 double Area(); // Method signature 6} 7 8class Circle : IShape 9{ 10 private double radius; 11 12 public Circle(double radius) 13 { 14 this.radius = radius; 15 } 16 17 public double Area() 18 { 19 return Math.PI * radius * radius; // Area of Circle 20 } 21} 22 23class Rectangle : IShape 24{ 25 private double width, height; 26 27 public Rectangle(double width, double height) 28 { 29 this.width = width; 30 this.height = height; 31 } 32 33 public double Area() 34 { 35 return width * height; // Area of Rectangle 36 } 37} 38 39class Program 40{ 41 static void Main(string[] args) 42 { 43 IShape shape; 44 45 shape = new Circle(5); 46 Console.WriteLine("Area of Circle: " + shape.Area()); // Output: Area of Circle: 78.53981633974483 47 48 shape = new Rectangle(4, 5); 49 Console.WriteLine("Area of Rectangle: " + shape.Area()); // Output: Area of Rectangle: 20 50 } 51}
Advantage of using Polymorphism
Benefit | Description |
---|---|
Code Reusability | Allows writing code that can work with objects of different types through a common interface or base class, reducing redundancy. |
Flexibility and Maintainability | Makes it easier to extend and maintain code by allowing new classes to be added without modifying existing code. |
Improved Readability | Leads to cleaner and more understandable code by abstracting details of specific implementations. |
Dynamic Method Resolution | Enables method calls to be resolved at runtime, allowing for dynamic behavior based on the actual object type. |
Reduction of Conditional Statements | Eliminates the need for complex conditional statements to determine which method to call, simplifying the code. |
Support for Interfaces and Abstract Classes | Encourages the use of interfaces and abstract classes, promoting clear design and better-structured code. |
Encapsulation | Promotes encapsulation by allowing objects to expose a common interface while hiding implementation details. |
Facilitates Testing and Mocking | Allows easy creation of mock objects for testing, enabling the simulation of behavior without relying on actual implementations. |