Run-Time Polymorphism in C#. Master the Polymorphism concept | by Rikam Palkar | May, 2022

Master the Polymorphism concept

Photo by Farzad Nazifi on Unsplash

In the last article, we saw what in God’s name Method Overloading is and how we can achieve it. Method overriding is the long-lost sibling of method overloading, but there is one fundamental difference that sets them apart.

This is part 2 of the Polymorphism series, where we are going to take a deep dive into polymorphism. The following are the 3 pieces of the puzzle.

  1. Compile-time polymorphism
  2. Run-time polymorphism
  3. Method hiding/ shadowing

We will compare overriding with overloading which will not only clear the confusion between these two but also give a better understanding of overriding.

Overloading means having multiple behavior where overriding means changing the behaviour.

Following are the key differences between overloading and overriding.

  1. In method overloading, we have to change the signature, whereas in method overriding we have to keep the same method signature including return-type and access specifier, it is a technique of reusability. Meaning, redefining the parent class method in the child class with exactly the same signature.
  2. Methods can be overloaded within the same class and within subclasses as well. Whereas overriding methods can only be overridden in a subclass, if you try to override a method within the same class using the same signature, you will get a compile-time exception shown below.

The Exception: Type ‘class name’ already defines a member called ‘method name’ with the same parameter types.

The most important rule: You can only override virtual or abstract methods, which means the parent class needs to decorate a method with ‘virtual’ or ‘abstract’ keywords that will allow the child class to override. You can not override methods that are not virtual or abstract.

Let’s start coding!

Rule number 1: Method signature should match including return-type and access specifier.

Listing 1: Overridden method GetOS()

Let’s call these methods with their type to see if this worked?

As you can see in the image-1 below, an instance of parent class SmartPhone is calling its version of GetOS() and printing message “Android” Instead, an instance of child class Iphone is calling its version of GetOS() and printing message “iOS”. This is because we’ve successfully overridden a method GetOS() in class IPhone.

Image 1: Parent and child classes objects calling their respective GetOS() method

Note: If you did not define a method in child class then the child class object will call the parent’s method. If you had a code that looks like listing 2, then you’ll get output that looks like image-2.

Listing 2: Method GetOS() is not overridden in child class

In the following image 2, you can see both parent and child class objects are calling parent class version of GetOs()

Image 2: Parent and child classes objects calling parent’s GetOS() method

Let’s see a few false scenarios down the line, which are important to understand!!

False scenario 1: what happens if I try to override a method with a different return type?

I get the following exception!

The Exception: ‘IPhone.GetOS()’: return type must be ‘string’ to match overridden member ‘SmartPhone.GetOS()’

Image 3: Compile time exception for different return type

False scenario 2: what happens if I try to override a method with a different access specifier?

I get the following exception!

The Exception: ‘IPhone.GetOS()’: return type must be ‘string’ to match overridden member ‘SmartPhone.GetOS()’

Image 4: Compile time exception for different access specifier

False scenario 3: what happens if I try to override a method which is not virtual or abstract?

I get the following exception of course!

The Exception: ‘IPhone.GetOS()’: return type must be ‘string’ to match overridden member ‘SmartPhone.GetOS()’

Image 5: Compile time exception thrown while overriding a non-virtual method

Here you might argue that I can simply skip the override keyword in the child class, yes you can but then it won’t be an overridden method. It will be an independent method that belongs to type ‘IPhone’.

And this is called method hiding when parent and child classes have the same method name with the same signature without ‘virtual’ or ‘abstract’ keywords then the child class hides the implementation of the parent’s class method.

Let’s take one more scenario, this was one of the interview questions as well. What if you have a multilevel inheritance? Something like listing 3:

Listing 3 : SmartPhone <- IPhone <- IphoneX

New grandchild class IphoneX has its own version of GetOS()So with the object of IPhoneX we get its version of GetOS().

Image 6: Grandchild is calling its own implementation of GetOS()

The real question is, what if grandchild IPhoneX doesn’t have a version of the overridden method? so with its object, it will refer to parent’s IPhone’s GetOS() or grandparent’s SmartPhone’s GetOS()? Let’s find out.

Listing 4 draws this scenario.

Listing 4: Grandchild missing overridden method
Image 7: Grandchild refers to the immediate overridden method

Remember this hierarchy of preferences : Local -> immediate parent -> their parent -> till the base class.

If you look at that, the grandchild ends up calling its parent’s version of GetOS(). This is because the grandchild first looked at itself if the overridden method exists. If not then it goes to the parent to see if a version of overridden method exists. As it did exist in the parent it stopped its search and accessed the method.

OK!! Now we know that it accesses the immediate method in the case of overridebut what happens when the method is virtual? Let’s see.

Consider the following scenario:

Listing 5: Grandchild has access to 2 virtual methods

Here also the same rule applies, it looks for the immediate parent and tries to resolve the matter. In the following image, you can see IPhoneX is also printing “iOS” which is its parent’s version of GetOS().

Image 8: Grandchild refers to the immediate virtual method parent’s

Just for fun let’s delete the parent version of GetOS() as well.

Listing 6: Grandchild has access to GrandParent’s virtual method
Image 9: Grandchild refers to the immediate virtual method which is GrandParent’s

Now that it has reached the base class SmartPhone and it did find the method with a matching signature, it stopped its searched and printed the output “Android”.

Leave a Comment