Polymorphism
Simplifying Polymorphism in C# with Examples
Polymorphism is a fundamental concept in object-oriented programming (OOP), and it can be understood more easily by breaking it down into its key components.
What is Polymorphism?
At its core, polymorphism means “many forms.” In the context of OOP, it allows you to work with different classes in a way that treats them as instances of a common base class. This makes your code more adaptable and versatile.
Two Types of Polymorphism
Polymorphism can be categorized into two types: static and dynamic.
Static Polymorphism (Compile-Time Polymorphism)
In static polymorphism, the decision about which function to use is made at compile time. It is also known as early binding. C# offers two techniques to implement static polymorphism: function overloading and operator overloading.
Function Overloading
Function overloading enables you to have multiple definitions of the same function name within the same scope. The definitions must differ by the types and/or the number of arguments in the argument list. You cannot overload function declarations that differ only by return type.
Here’s an example demonstrating function overloading:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
using System; namespace PolymorphismApplication { class Printdata { void print(int i) { Console.WriteLine("Printing int: {0}", i); } void print(double f) { Console.WriteLine("Printing float: {0}", f); } void print(string s) { Console.WriteLine("Printing string: {0}", s); } static void Main(string[] args) { Printdata p = new Printdata(); // Call print to print integer p.print(5); // Call print to print float p.print(500.263); // Call print to print string p.print("Hello C++"); Console.ReadKey(); } } } |
Examples-
Example 1: Function Overloading with Different Parameter Types
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class MathOperations { public int Add(int a, int b) { return a + b; } public double Add(double a, double b) { return a + b; } } |
In this example, we have overloaded the Add
method to work with both integers and doubles. Depending on the argument types, the appropriate version of the method will be called.
Example 2: Function Overloading with Different Number of Parameters
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class Calculator { public int Add(int a, int b) { return a + b; } public int Add(int a, int b, int c) { return a + b + c; } } |
Here, we have two Add
methods that accept different numbers of parameters. You can call the appropriate method based on how many values you want to add together.
Tricky Question 1: What happens if you have two overloaded methods with the same parameter types but different return type?
Consider the following code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class MyClass { public int Multiply(int x, int y) { return x * y; } public double Multiply(int a, int b) { return (double)(a * b); } } |
Solution: This will result in a compilation error because C# does not allow function overloading based solely on the return type. The compiler cannot differentiate between these two methods based on the parameter types alone, as both methods take two integers.
Tricky Question 2: What happens when you have overloaded methods with optional parameters?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class MyMath { public int Add(int a, int b) { return a + b; } public int Add(int a, int b, int c = 0) { return a + b + c; } } |
Solution: When calling the Add
method, if you provide two arguments, the first version of the method (with two parameters) will be called. If you provide three arguments, the second version (with three parameters) will be called. The optional parameter c
with a default value of 0 allows for this behavior.
Tricky Question 3: Can you overload a method by just changing the return type?
No, as addressed before by an example, you cannot overload a method in C# solely by changing its return type. The compiler does not consider the return type when resolving which overloaded method to call. It relies on the method name and the parameter list to determine the appropriate method.
Tricky Question 4: What happens if there is an ambiguity between overloaded methods?
Consider this code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class MyClass { public int Multiply(int x, int y) { return x * y; } public int Multiply(int a, int b) { return a * b; } } |
Solution: In this case, you will get a compilation error
because there is an ambiguity between the two methods. Both methods have the same name and the same parameter types, so the compiler cannot determine which one to call based on the method signature alone. You should rename one of the methods to resolve the ambiguity.
Function overloading is a powerful feature in C#, but you should be careful to ensure that overloaded methods have distinct parameter lists to avoid ambiguity and compilation errors.
B – Dynamic Polymorphism (Run-Time Polymorphism)
Dynamic polymorphism, on the other hand, determines which function to use at runtime. It is also known as late binding. This type of polymorphism is achieved through abstract classes and virtual functions.
Example: Dynamic Polymorphism Virtual
Override
Keywords
What is Virtual
We use this to mark function virtual, so it can be override in base class to achieve runtime polymorphism.
What is Override
Use in child class method, which we are going to override from base.
In this example, we’ll create a hierarchy of shapes with a base class Shape
and derived classes Circle
and Rectangle
. We’ll use dynamic polymorphism to demonstrate how method overriding works.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
using System; class Shape { public virtual void Draw() { Console.WriteLine("Drawing a shape"); } } class Circle : Shape { public override void Draw() { Console.WriteLine("Drawing a circle"); } } class Rectangle : Shape { public override void Draw() { Console.WriteLine("Drawing a rectangle"); } } class Program { static void Main() { Shape[] shapes = new Shape[3]; shapes[0] = new Circle(); shapes[1] = new Rectangle(); shapes[2] = new Shape(); foreach (var shape in shapes) { shape.Draw(); } } } |
Output:
1 2 3 4 |
Drawing a circle Drawing a rectangle Drawing a shape |
In this example, we create an array of Shape
objects and assign instances of Circle
, Rectangle
, and Shape
to it. When we call the Draw
method on each shape, dynamic polymorphism ensures that the correct version of the Draw
method is called based on the actual type of the object using Virtual and Override keywords.
0 Comments