Table of Contents
Introduction
In a previous article we have seen about Polymorphism in Object Oriented Programming and how compile-time polymorphism involves method overloading while a runtime polymorphism employs method overriding.
In this article, let’s talk in particular about method overloading & overriding in c#.
We will also look at difference between overloading & overriding.
Method overloading and overriding are two distinct characteristics of any Object oriented programming which involves a same method name with variation in either how they’re composed or how they’re invoked.
Together, both help in extensibility and varied implementations of components.
What is Method Overloading?
Overloading is a phenomenon, where a method or a constructor can have different variants or flavors for itself, by means of variations in its method “signature”.
What is a Method Signature?
A signature implies the below features of a method:
- The number of parameters
- The data types of the parameter
A method Signature doesn’t comprise of its return type and it shall NOT be considered for method overloading
In short, overloading refers to “one thing in many forms”.
Now we can have an overloading method, or an overloading constructor since these two are the only things in a class which can take a parameter.
How to implement method Overloading in C#?
For example, consider the below Beast class with a single method Sound() and a number of variations of its “signature”
public class Beast
{
// actual method
public void Sound()
{
Console.WriteLine("Beast makes a sound - grr");
}
// variation in number of parameters
public void Sound(string sound)
{
Console.WriteLine("Beast makes a sound of passed string {sound}");
}
// variation in the type of parameter
public void Sound(object sound)
{
Console.WriteLine("Beast makes a sound of passed object {sound}");
}
// variation in type and number of parameters
public void Sound(string sound, string name)
{
Console.WriteLine(
"Beast makes a string sound {sound} and its name is {name}");
}
// variation in type and number of parameters
public void Sound(object sound, string name)
{
Console.WriteLine(
"Beast makes an object sound {sound} and its name is {name}");
}
}
Now when we run this example, we can observe that the resultant method call depends on what kind of argument we pass and how many arguments we pass.
It will result in the below output.
public class NewProgram
{
public static void Main(string[] args)
{
Beast b = new Beast();
// call the default
// Sound() method
b.Sound();
// call Sound()
// with a string param
b.Sound("PowPow");
// call Sound()
// with an int param
b.Sound(12345);
// call Sound()
// with two string arguments
b.Sound("PowPow", "Cheetah");
// call Sound()
// with a string and a number
b.Sound(12345, "Cheetah");
}
}
// Output
Beast makes a sound - grr // default method
Beast makes a sound of passed string PowPow // with a string argument - overload 1
Beast makes a sound of passed object 12345 // with an integer argument - overload 2
Beast makes a string sound PowPow and its name is Cheetah // with two string arguments - overload 3
Beast makes an object sound 12345 and its name is Cheetah // with a string and a number argument - overload 4
The decision of which method overload needs to be called for the given set of arguments is taken at compile-time. Overloading is also called Compile-time polymorphism.
When Overloading can be helpful?
When an implementation needs to supply more than one variant of a functionality for the same given name, without willing to have different method names for each variation in the functionality, we can simply opt for method overloading where we can define multiple implementations of a same method name, and the calling client need not worry about the method it needs to be called for the required functionality.
public class Figure
{
// area of a square
public double Area(double side)
{
return side * side;
}
// area of a rectangle
public double Area(double length, double breadth)
{
return length * breadth;
}
// area of a circle
public double Area(double radius, bool isCircle)
{
if (isCircle)
return 3.14 * Area(radius);
else
return Area(radius);
}
}
Method Overloading with Optional Parameters
If a method has an overload with optional parameters, which overload is called when parameters are supplied?
When method overloading involves optional parameters, the compiler gives preference to the method with no optional parameters involved first.
public class SomeComponent
{
public void Add(int a, int b, int c = 5)
{
Console.WriteLine("{a} + {b} + {c} = {a + b + c}");
}
public void Add(int a, int b)
{
Console.WriteLine("{a} + {b} = {a + b}");
}
}
// The outputs when called with the below arguments
p.Add(2, 3, 6); // 2 + 3 + 6 = 11
p.Add(4, 5); // 4 + 5 = 9
Even though the overload with optional parameter c is a valid match the compiler picks up the overload with two arguments for the execution.
In such cases, Compiler gives priority to variable arguments over substitute constants.
What is Method Overriding?
Overriding is a phenomenon involving two or more methods of the same name but in different classes which are related to each other as base and derived types.
These methods share the same name and same signature but with different implementations depending on their types. And the base type method is “overridden” by the same method composition of its derived type when being called at the client.
How to implement method Overriding in C#?
For example, let’s assume an Animal class which has a single method Sound() that takes a single argument sound of type string.
Now this Animal is a base type with two three different classes Cat, Dog and Lion which too are composed of method Sound() which too takes a single argument sound of type string. This setup is shown as below.
namespace WorkConsoleApp
{
public class Animal
{
public virtual void Sound(string sound)
{
Console.WriteLine("Animal makes Sound - {sound}");
}
}
public class Cat : Animal
{
public override void Sound(string sound)
{
Console.WriteLine("Cat makes Sound - {sound}");
}
}
public class Dog : Animal
{
public override void Sound(string sound)
{
Console.WriteLine("Dog makes Sound - {sound}");
}
}
public class Lion : Animal
{
public override void Sound(string sound)
{
Console.WriteLine("Lion makes Sound - {sound}");
}
}
}
When you look at it, each of the types (Dog, Cat and Lion) can exist individually and can be referred to individually for their method Sound().
But at the caller’s end, the caller would never know what is the underlying implementation he’s invoking.
Thus he would just call the method Sound() over a reference variable animal which is of basetype Animal, and would have its value be assigned via some functionality (say a factory, or a constructor injection).
public class AnimalCaller
{
private Animal animal;
public AnimalCaller()
{
// assign to Dog instance
this.animal = new Dog();
}
public void CallAnimal()
{
animal.Sound("Cat");
}
}
The decision of what method needs to be called for a variable of a specific base type is taken depending on what derived type instance is passed into it and is taken during runtime. Overriding is also called runtime Polymorphism
When Overriding can be helpful?
Overriding is one of the more important concepts of code by abstractions and can greatly help in code extensibility.
At a later point of time, the caller needs to add an additional implementation of the base Animal type in the form of an animal Crocodile, which shall also have a Sound() method.
We can simply make this Crocodile class a derivative of the base Animal type and then override the actual Sound() functionality which the animal provides with its own flavor and then substitute this new implementation for the abstract type argument the caller expects.
This can greatly help with developing extensive components and is also one of the SOLID principles, the Open/Closed principle. Method Overriding makes all this possible because of this characteristic.
Differences between Method Overloading and Overriding
Method Overloading | Method Overriding |
Same Method Name but difference in Signature | Same Method Name and Signature |
Doesn’t involve inheritance | Classes having same method signatures are related as parent and child |
Overloading is Compile-time Polymorphism | Overriding is Runtime Polymorphism |
Overloading enables us to provide variants of same method within same class | Overrding involves multiple classes and can help in extending features with new child classes and overriding definitions |