C# Polymorphism


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:

csharp
1using 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 method Speak().
  • The Dog and Cat classes override the Speak() method to provide their specific implementations.
  • In the Main method, we create an Animal reference and assign it to a Dog and Cat object. When we call Speak(), 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:

csharp
1using 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 overloaded Add 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:

csharp
1using 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

BenefitDescription
Code ReusabilityAllows writing code that can work with objects of different types through a common interface or base class, reducing redundancy.
Flexibility and MaintainabilityMakes it easier to extend and maintain code by allowing new classes to be added without modifying existing code.
Improved ReadabilityLeads to cleaner and more understandable code by abstracting details of specific implementations.
Dynamic Method ResolutionEnables method calls to be resolved at runtime, allowing for dynamic behavior based on the actual object type.
Reduction of Conditional StatementsEliminates the need for complex conditional statements to determine which method to call, simplifying the code.
Support for Interfaces and Abstract ClassesEncourages the use of interfaces and abstract classes, promoting clear design and better-structured code.
EncapsulationPromotes encapsulation by allowing objects to expose a common interface while hiding implementation details.
Facilitates Testing and MockingAllows easy creation of mock objects for testing, enabling the simulation of behavior without relying on actual implementations.

Talk to us?

Post your blog