Sunday, March 03, 2024 21:55

Table of contents >> Object Oriented Programming > Polymorphism


The third fundamental principle of Object Oriented Programming is called polymorphism.  At a fundamental level, polymorphy refers to the ability of having many forms, or to transform into many forms. It comes from the Greek terms poly, which means “multiple”, and morph, which means “shape” or “form”.

Despite having a discouraging definition, polymorphism is quite simple to understand. Lets consider a base class:

So, nothing complicated. Just a class named Animal, with two methods, Eat() and Sleep(), which are common to all animals.

Lets create two classes which represent concrete animals:

So far, so good, both Cat and Dog are classes that inherit from Animal, which means they are also of type Animal, aside of their own type, and which also means they both now posses the methods of Animal class, Sleep() and Eat().

Imagine now that you would want to have a random number of both cats and dogs instances, and you would need to keep track of them. The first impulse would be to create two arrays, one of type Cat, one of type Dog, and manipulate them separately:

This could be one approach, but it would not be entirely correct; or, more to the point, it wouldn’t be the most efficient one. What if you’d have hundreds of animal types? That would rapidly become quite cumbersome.

On the other hand, lets revision what polymorphism is, at its core: “the ability of something to take (or have) multiple forms”. Well, isn’t that great? Isn’t that exactly what we’d need in this situation? So, the first step towards starting to use polymorphic behavior in our codes is to look for behavior that is common to all our objects (they are all animals, they are all sleeping, eating), even if the specific implementation of the common behavior differs from object to object. In other words, even if a cat is not a dog, and a dog is not a cat, they are both animals. They have the same behavior of sleeping and eating, even if that behavior is implemented differently for them. Polymorphism doesn’t care that cats eat mice and cows eat grass, it only cares that both cats and cows have the ability to eat, which is a polymorphic action (it can take many forms). In this new light, Animal is a polymorphic object. We don’t care if it’s a cat or a whale, we only care that they are both animals.

How does that help us? Have a look at this:

If you remember from the chapter Arrays, we cannot instantiate an array with a different type than the type of which we declared it. So, how was I able to add a Cat on the first position of the array of type Animal, and then, add a Dog on the second position?! If I declared it of type Animal, I should be allowed to only add instances of type Animal in its elements! And then, just like that, I was able to call the Eat() method on all animals in the array, without carrying of their actual type!

Et voila, the magic of polymorphism! Because both Dog and Cat inherit from Animal, they ARE animals too, in addition of being cats and dogs. This behavior of inheritance is also known as a “is a” relationship. A cat “is an” animal. You will also find another definition of polymorphism, which basically describes same concepts, with different words: polymorphism allows treating objects of a derived class as objects of its base class.

Now, you should know what polymorphism basically is. But, be aware, it can be tricky. Consider this example:

The above example will make your program crash. Why? First of all, because polymorphism is not bidirectional. Any cat IS an animal, but NOT any animal is a cat. So, we can only perform polymorphic behavior on generic, common concepts, not on concrete implementations. In simple words, animal can take many forms, cats are just cats, a single form of animal.

Because in the above example we also added a Dog on the second position of the array, the program crashed when we used the foreach loop in the form of Cat. A dog is certainly not a cat, and the program will not be able to cast an object of type dog into a cat.

The way to avoid this is by using a special C# keyword, the is operator:

Polymorphism in C# is classified in two main types:

  • compile time polymorphism, also known as static polymorphism, which basically means the ability to call a method with multiple types of parameters (method overloading), and also operator overloading.
  • runtime polymorphism, also known as dynamic polymorphism, or late binding, which can be achieved either in the form explained above (being able to use similar properties of different objects), or by method overriding, which means implementing custom functionality for common behaviors (all animals eat, which is an action defined in the base class, but which can be overridden in child, concrete classes, with custom eating behavior for each animal type).

Polymorphism can bear strong resemblance to abstraction, but it is mostly related to actually overriding methods in derived classes, in order to change their original behavior inherited from the base class. Abstraction is associated with creating an interface of a component or functionality (defining a role). But, I will explain this in greater details when I will write about interfaces.

Tags: , , , , ,

Leave a Reply

Follow the white rabbit