Polymorphism in general life
In general terms, polymorphism refers to the ability of a single entity to take multiple forms or exhibit multiple behaviors. It is a concept that can be observed in various aspects of our everyday lives, not just limited to programming. Let's explore some real-life examples to understand the essence of polymorphism.
For e.g Electrical Outlets
Consider electrical outlets in your home. You can plug in various devices like a laptop, smartphone, or a vacuum cleaner into the same outlet. The outlet serves a common interface for different devices, allowing them to function despite their diverse characteristics. This is akin to polymorphism, where a single entity (electrical outlet) supports multiple forms (devices with different power requirements).
Polymorphism in object-orinented programming
Polymorphism is a feature of object-oriented programming that allows an entity (such as a method or an operator) to have different forms or behaviors depending on the context. The word polymorphism means “many forms” in Greek. Polymorphism can be achieved in C# through two ways: compile-time polymorphism and run-time polymorphism.
Compile-time polymorphism
Compile-time polymorphism, also known as static polymorphism, is when the compiler determines which method or operator to invoke at compile time, hence it is also called as early binding where compiler knows which method of class to call at compile time only. Here compiler decide which method to call using class of object and method or operator and its signature (number of argument, and type of arguments). Here please note that return type of method cannot be considered for mthod overloading, if two method has exact same signature and different return type, it's not considered as method overriding and compiler generates error message for same.
Method overloading is when you have multiple methods with the same name but different parameters in a class. For e.g. in below example Add method in Calculator class is overloaded to accept two integer type of argument in first place and in second place it is accepting three parameter of double type.
class Calculator
{
// Method with two integer parameters
public int Add(int a, int b)
{
return a + b;
}
// Method with three double parameters
public double Add(double a, double b, double c)
{
return a + b + c;
}
}
class Program
{
static void Main()
{
Calculator calculator = new Calculator();
// Calls the first Add method
int result1 = calculator.Add(5, 10);
// Calls the second Add method
double result2 = calculator.Add(2.5, 3.5, 4.0);
Console.WriteLine($"Result1: {result1}");
Console.WriteLine($"Result2: {result2}");
}
}
Output
If we try to add another Add method with same signature with different return type compiler gives error, as its not allowed
Operator overloading is when you redefine the behavior of an operator for a custom type. For example, you can overload the + operator to perform string concatenation or matrix addition.
Run-time polymorphism
Run-time polymorphism, also known as dynamic polymorphism, is when the method or operator to invoke is determined at run time based on the type of the object. This can be done by using inheritance and virtual methods. Inheritance is when you create a new class from an existing class and inherit its members. Virtual methods are methods that can be overridden by derived classes to provide different implementations. For example, you can have a virtual method called Draw() in a base class called Shape, and override it in derived classes such as Circle, Rectangle, and Triangle.
using System;
// base class
class Shape
{
// virtual method
public virtual void Draw()
{
Console.WriteLine("Drawing a shape");
}
}
// derived class
class Circle : Shape
{
// override method
public override void Draw()
{
Console.WriteLine("Drawing a circle");
}
}
// derived class
class Rectangle : Shape
{
// override method
public override void Draw()
{
Console.WriteLine("Drawing a rectangle");
}
}
// derived class
class Triangle : Shape
{
// override method
public override void Draw()
{
Console.WriteLine("Drawing a triangle");
}
}
class Program
{
static void Main(string[] args)
{
// create an array of Shape objects
Shape[] shapes = new Shape[4];
shapes[0] = new Shape();
shapes[1] = new Circle();
shapes[2] = new Rectangle();
shapes[3] = new Triangle();
// loop through the array and call the Draw method
foreach (Shape shape in shapes)
{
shape.Draw();
}
}
}
Here is an example of polymorphism using inheritance and virtual methods in C#.
Drawing a shape
Drawing a circle
Drawing a rectangle
Drawing a triangle
Polymorphism is deeply ingrained within the .NET Framework. Here's an example demonstrating polymorphism using the Stream
class and its derived classes.
using System;
using System.IO;
class Program
{
static void Main(string[] args)
{
// Create a FileStream object, but assign it to a Stream variable
Stream stream = new FileStream("example.txt", FileMode.Open);
// Read from the stream
byte[] buffer = new byte[1024];
int bytesRead = stream.Read(buffer, 0, buffer.Length);
// Convert the bytes read to string and display
Console.WriteLine("Data read from the file:");
Console.WriteLine(Encoding.UTF8.GetString(buffer, 0, bytesRead));
// Close the stream
stream.Close();
}
}
In this example, we are using polymorphism through the Stream
class and its derived class FileStream
. We declare a Stream
variable and instantiate it with a FileStream
object. This is possible because FileStream
inherits from Stream
, allowing us to treat a FileStream
object as a Stream
object. Later, we use the Read
method of the Stream
class to read data from the file. At runtime, the Read
method behaves differently based on the actual type of the object (FileStream
in this case), demonstrating polymorphic behavior.
Let's assume "example.txt" contains the following text.
"Hello, this is an example file for demonstrating polymorphism in .NET."
Then the output of the program will be:
Data read from the file:
"Hello, this is an example file for demonstrating polymorphism in .NET."
Summary
Polymorphism means one name multiple forms or one function name doing different thing as per context (i.e. number of parameters or type of parameters), types of polymorphism are compile time polymorphism and runtime polymorphism. Compile time polymorphism or early binding is achieved by doing function overloading and operator overloading. Runtime polymorphsim is achieved using function overriding, base class with virtual function and derived class overriding it.