Compile-Time Polymorphism in C#

Object oriented programming is a rabbit hole where polymorphism is just another thread in the hole, let’s see how deep can it go.

Rikam Palkar
Better Programming
Published in
7 min readMay 14, 2022

--

Photo by Thalia Tran on Unsplash

This article will take you on a journey to unravel the mystery of compile time polymorphism . Plus in C# we have just the right material to make it worse, with method overloading and overriding, we also have the concept of method hiding, which makes the whole idea of polymorphism a bit baffling. And that’s why I am going to clear the confusion by breaking down the idea into 3 parts.

This is a series of 3 articles 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

Why it’s called compile-time polymorphism?

Because the compiler knows which method to call at compile time based on method’s signature. This dependency is solved at compile time. Ok! this sounds good in theory but let’s see how it works practically.

HOLD ON SEC!!, what did you mean by method signature, though?

Well, there are 4 pieces to any method.

  1. Method name,
  2. Return type,
  3. Parameter/s,
  4. The access specifier
Listing 1: Method signature

There is a huge argument between the developer’s community, whether return type should be part of method signature or not!!

How about we clear that confusion from Microsoft?

As per Microsoft, “A return type of a method is not part of the signature of the method for the purposes of method overloading. However, it is part of the signature of the method when determining the compatibility between a delegate and the method that it points to”.

In simple words, while “overriding a method”, return type is taken into consideration as a part of method signature.

while “overloading a method”, it is simply ignored.

Let’s understand polymorphism step by step with actual coding and with coding examples we will learn the behaviors to support the argument above.

So take a deep breath and join me on this journey.

There are few rules you need to be aware of while overloading a method.

  1. Type of parameters should be different, and the name of the parameter doesn’t matter.
  2. Number of parameters should be different.
  3. Order of the parameters should be different.
  4. Last but not the least, return-type, access specifiers or any of other fancy keywords such as static, abstract, sealed, virtual are not taken into consideration.

To demonstrate these rules, I am going to code following UML throughout the article, this will touch upon almost all the aspects of method overloading.

Let’s design some costumes for some of our favorite superheroes?

Imagine we are designing a class superhero in which our task is to customize a suit. There could be different arguments coming into play, color of the suit, type of suit, or whether the hero likes to wear his underwear out or not etc. Let us go ahead and design a class to fulfil these points.

In the following code snippet, we are designing a suit for a “Superman”.

Listing 2: class SuperHero

This code works as long as you want your default superhero to-be superman. But let’s face the fact, he ain’t that great. So in order to customize a suit for another superhero, we can create a separate method CustomizeSuit().

Say I’ve typeOfSuit and I want to customize that, it could be a leather or a metal suit. In order to code this I can simply create a method which will accept one string type parameter which would be typeOfSuit.

Listing 3: Method CustomizeSuit with one parameter

Rule #1: Type of parameter/s should be different, the name of the parameter/s doesn’t matter.

My superhero expresses an interest in a metal suit, now he obviously can’t wear underwear on the top of it. So I can simply overload the CustomizeSuit() method to accept different type of parameter. In here, we are overloading a method which accepts boolean type, if the value of this parameter true then our hero is going to wear an underwear outside else inside.

Listing 3: Overloaded method CustomizeSuit with boolean parameter

Rule #2: Number of parameters should be different.

Some users want to either wants to customize the type of suit or decide the placement of the underwear but others might be interested in changing both together. We can achieve this simply by overloading a method with a different number of parameters.

Listing 4: Overloaded method CustomizeSuit with 2 parameters string and boolean

Rule #3: Order of the parameters should be different.

You can have the same number of parameters but the order must be different, for example I can take code from listing 4 and do this.

Listing 5: Overloaded method CustomizeSuit with 2 parameters boolean and string

Listing 5 explanation: I just interchange the order of parameters in listing 5. Compiler looks for the index of the parameter and its type. If Type is different at same index then it’s a valid overloaded method.

Another variation could be as listing 6

Listing 6: Overloaded method CustomizeSuit with 2 parameters string and string

Rule #4: Last but not the least , return type, access specifiers or any of other fancy keywords such as static, abstract, sealed, virtual means nothing.

Let’s go fully wild on that one, shall we?

Rule #4.1: Return type is not taken into consideration.

In the following example everything is the same between 2 methods, except the return-type. If you have 2 methods with the same signature but different return-type, it is not considered as method overloading. Compiler will throw a compile-time exception.

Listing 7: Overloaded method CustomizeSuit with same signature but different return types

The Exception: Type ‘SuperHero’ already defines a member called ‘CustomizeSuit’ with the same parameter types.

Image 1: Snapshot of exception where return types are being ignored

Rule #4.2: Access specifiers are ignored

Well rule 4.1 was pretty cool, Now for rule number 4.2, we need to change the access specifiers.

Listing 8: Overloaded method CustomizeSuit with same signature but different access specifier

Same good exception!!

Image 2: Snapshot of exception where access specifiers are being ignored

Rule #4.3: Special keywords are ignored

In the following snap you would see, none of the keywords are working with an overloaded method. So decorating a method with these keywords means nothing to the concept of overloading.

Note: For the abstract method, I’ve marked class as abstract.

Image 3: Few examples with different decorators on method CustomizeSuit

Above all methods falls under the same exception.

Type ‘SuperHero’ already defines a member called ‘CustomizeSuit’ with the same parameter types

Image 4: Compile time exceptions

We have come a long way with method overloading, there is one more important concept I would like to throw in there as a bonus point.

Method overloading with inheritance

The following code snippet is a parent class SuperHero. It contains every overloaded method that we’ve discussed so far.

Listing 9: class SuperHero with 4 overloaded methods

Let’s create a child class which will inherit the parent class SuperHero

Following class IronMan is the child of class SuperHero. which is very elegantly overloading a CustomizeSuit() method with 3 parameters.

Listing 10: class IronMan with 1 overloaded method

Yeah!! You heard it right, method overloading is not restricted within a same class.

I have created an instance of both parent and a child class. Have a look at the following image 5, I get 4 overloaded methods with parent’s object.

Image 5: Parent’s object showing number of overloaded method CustomizeSuit

Let’s see what number we get when we try to call CustomizeSuit() with a child’s object. As you can see in the image 6, child class A.K.A. IronMan has one more extra count, which is referring to the method we just created in listing 10. Hence proved, you can actually overload a method from different classes.

Image 6: Child’s object showing a number overloaded method CustomizeSuit

Even though I can overload a parent’s method in a child class, still there is one thing we should be aware of.

Note: The four rules that I mentioned above are only applicable within the same class.

Let’s not get confused here! Let me demonstrate this with an example. Have a look at the following two images 7 and 8.

In the image below, the CustomizeSuit() method is defined in class SuperHero and the same method is again defined in class IronMan and it seems the compiler has no problem with it.

Object of class SuperHero will call the it’s own version of CustomizeSuit() method, and object of class IronMan would have extra count overloaded method but it will still call its own implementation of CustomizeSuit() method.

So point being, you can have the same signature of an overloaded method only if you are in a different type.

Image 7: Two classes have methods with same signatures

But you can’t have the same signature within the same type.

Look, how compiler reacts in image 8. It gives me an error when I am overloading a method with the same signature within the same class. So remember those four rules are applicable within the same class.

Image 8: Method with same signature is defined in the same type

Conclusion

Yes, Method overloading could be quite overwhelming, when we try to cover all the possibilities but once you have the basics fundamentals clear you will be able to understand its purpose in object oriented programming. In this article we deep dived covering all aspects with different examples to understand what is acceptable and what is not when it comes to compile time polymorphism.

Want to Connect?Hit me up on LinkedIn.

--

--