11.4. Polymorphism

Polymorphism means “many-shaped”, and it occurs when we have classes that are related to each other by inheritance. Polymorphism refers to the ability of a single interface to be used to refer to multiple implementations of a particular behavior (functionality or “method”).

In C#, polymorphism can be achieved through inheritance, interfaces, and virtual methods. Inheritance allows us inheriting fields and methods from a base/parent class. Polymorphism further allows use to modify and use those methods to perform different tasks. This is called method overriding, meaning to override the definition of the method in the base/parent class. In this case here, you are implementing method name that you inherit from a parent class, you have to use the same method signatures.

There will be time when you need to define two or more methods sharing the same method name in the same class (as opposing to method overriding, where method modification happens in the child classes). For example, you want to create an “AddTwo” method in your math operation class and you want AddTwo to intake integers, float, and decimal types. This is a form of polymorphism [1] called method overloading, in which you need to provide different method signatures (such as data types) in order to distinguish the methods.

11.4.1. Method Overriding: virtual/override/base

Think of a base class called Animal that has a method called animalSound(). Derived classes of Animals could be Pig, Cat, Dog - and they can have their own implementation of the same animalSound() (the pig oinks, and the cat meows, etc.):

 1class Animal                // Base class (parent)
 2{
 3    public void animalSound()
 4    {
 5        Console.WriteLine("The animal makes a sound");
 6    }
 7}
 8
 9class Pig : Animal          // Derived class (child)
10{
11    public void animalSound()
12    {
13        Console.WriteLine("The pig says: wee wee");
14    }
15}
16class Dog : Animal          // Derived class (child)
17{
18    public void animalSound()
19    {
20        Console.WriteLine("The dog says: bow wow");
21    }
22}
23class Program
24{
25    static void Main(string[] args)
26    {
27        Animal myAnimal = new Animal(); // Create a Animal object
28        Animal myPig = new Pig();       // Create a Pig object
29        Animal myDog = new Dog();       // Create a Dog object
30        myAnimal.animalSound();         // The animal makes a sound
31        myPig.animalSound();            // The animal makes a sound
32        myDog.animalSound();            // The animal makes a sound
33        myCat.animalSound();            // The animal makes a sound
34    }
35}

The code output above is not what you expected. Different animals should make different sounds, but here Pig and Dog are making the same sound. This happens because the base class method overrides the derived class method, when they share the same name.

For the derived (child) to override the base class method, you need to

  1. add the virtual keyword to the method in the base class; and

  2. use the override keyword for each derived class methods.

 1class Animal                            // Base class (parent)
 2{
 3    public virtual void animalSound()   // virtual
 4    {
 5        Console.WriteLine("The animal makes a sound");
 6    }
 7}
 8
 9class Pig : Animal                      // Derived class (child)
10{
11    public override void animalSound()  // override
12    {
13        Console.WriteLine("The pig says: wee wee");
14    }
15}
16
17class Dog : Animal                      // Derived class (child)
18{
19    public override void animalSound()  // override
20    {
21        Console.WriteLine("The dog says: bow wow");
22    }
23}
24
25class Cat : Animal                      // Derived class (child)
26{
27    public void animalSound()
28    {
29        animalSound();
30        // or base.animalSound();       // the base keyword refer
31    }
32}
33
34
35class Program
36{
37    static void Main(string[] args)
38    {
39        Animal myAnimal = new Animal(); // Create a Animal object
40        Animal myPig = new Pig();       // Create a Pig object
41        Animal myDog = new Dog();       // Create a Dog object
42        Animal myDog = new Cat();       // Create a Cat object
43
44        myAnimal.animalSound();         // The animal makes a sound
45        myPig.animalSound();            // The pig says: wee wee
46        myDog.animalSound();            // The dog says: bow wow
47        myCat.animalSound();            // The animal makes a sound
48    }
49}

Now you have achieved method overriding by marking the method in the base class virtual and the method in the derived class override.

Note

The content in this section discuss advanced scenarios and is here for your reference.

In C#, there are a few rules to follow when using polymorphism: [2]

  1. A class can only inherit from a single base class, but it can implement multiple interfaces.

  2. A method marked as virtual in the base class can be overridden in a derived class using the override keyword.

  3. If a derived class wants to call the implementation of a virtual method from the base class, it can use the base keyword.

  4. If a derived class wants to prevent a virtual method from being overridden in further derived classes, it can use the sealed keyword.

  5. If a derived class wants to provide its own implementation of a virtual method, but also wants to call the implementation from the base class, it can use the base keyword in the implementation.

 1public class Shape
 2{
 3    public virtual void Draw()
 4    {
 5        Console.WriteLine("Drawing a shape");
 6    }
 7}
 8
 9public class Circle : Shape
10{
11    public override void Draw()
12    {
13        base.Draw();                    // Call the implementation in the base class
14        Console.WriteLine("Drawing a circle");
15    }
16}
17
18public class Rectangle : Shape
19{
20    public sealed override void Draw()  // note the "sealed" keyword
21    {
22        Console.WriteLine("Drawing a rectangle");
23    }
24}
25
26public class Triangle : Shape
27{
28    public override void Draw()
29    {
30        Console.WriteLine("Drawing a triangle");
31    }
32}
33
34public class Square : Rectangle
35{
36    // This will cause a compile-error because the Draw method is sealed in the Rectangle class
37    public override void Draw()
38    {
39        Console.WriteLine("Drawing a square");
40    }
41}

11.4.2. Method Overloading

With method overloading, multiple methods can have the same name with different signatures/parameters.

For example, consider the following example, which your use two methods that add numbers of different data types: [3]

static int PlusMethodInt(int x, int y)
{
    return x + y;
}

static double PlusMethodDouble(double x, double y)
{
    return x + y;
}

static void Main(string[] args)
{
    int myNum1 = PlusMethodInt(8, 5);
    double myNum2 = PlusMethodDouble(4.3, 6.26);
    Console.WriteLine("Int: " + myNum1);
    Console.WriteLine("Double: " + myNum2);
}

Obviously you are performing the same operations in the preceding code, with the only difference been the data types. So, instead of creating two different methods, you may overload a PlusMethod method to work for both int and double:

static int PlusMethod(int x, int y)
{
    return x + y;
}

static double PlusMethod(double x, double y)
{
    return x + y;
}

static void Main(string[] args)
{
    int myNum1 = PlusMethod(8, 5);
    double myNum2 = PlusMethod(4.3, 6.26);
    Console.WriteLine("Int: " + myNum1);
    Console.WriteLine("Double: " + myNum2);
}

Now, with method overloading, you can call PlusMethod() with different data types (int or double) and C# will choose the correct method to run according to their signatures even if both methods have the same name.

Another example below shows you that, in addition to data types, you may even change the number of parameters or even the operators (the + in the fourth Add() method means).

namespace IntroCSCS
{
    class MethodOverLoading
    {
        public int Add(int num1, int num2)
        {
            return (num1 + num2);
        }
        public int Add(int num1, int num2, int num3)
        {
            return (num1 + num2 + num3);
        }
        public float Add(float num1, float num2)
        {
            return (num1 + num2);
        }
        public string Add(string value1, string value2)
        {
            return (value1 + " " + value2);
        }
    }
}

namespace IntroCSCS
{
    class Program
    {
        static void Main(string[] args)
        {
            MethodOverloading mol = new MethodOverloading();
            Console.WriteLine("Add two int parameter: " + mol.Add(3, 2));
            Console.WriteLine("Add three int parameter: " + mol.Add(3, 2, 8));
            Console.WriteLine("Add two float parameter: " + mol.Add(3f, 22f));
            Console.WriteLine("Add two string parameter: " + mol.Add("hello", "world"));             }

            // output:
            // Add two int parameter: 5
            // Add three int parameter: 13
            // Add two float parameter: 25
            // Add two string parameter: hello world
    }
}

Footnotes