Polymorphism is one of the core concepts of Object-Oriented Programming (OOP) in C#. It allows objects to be treated as instances of their parent class rather than their actual class. The two main types of polymorphism in C# are compile-time (or static) polymorphism and runtime (or dynamic) polymorphism.

Key Concepts

  1. Compile-time Polymorphism (Method Overloading)

    • Achieved by method overloading and operator overloading.
    • The method to be invoked is determined at compile time.
  2. Runtime Polymorphism (Method Overriding)

    • Achieved by method overriding using inheritance and interfaces.
    • The method to be invoked is determined at runtime.

Compile-time Polymorphism

Method Overloading

Method overloading allows a class to have multiple methods with the same name but different parameters.

public class MathOperations
{
    public int Add(int a, int b)
    {
        return a + b;
    }

    public double Add(double a, double b)
    {
        return a + b;
    }

    public int Add(int a, int b, int c)
    {
        return a + b + c;
    }
}

Explanation

  • The Add method is overloaded with different parameter lists.
  • The correct method is chosen based on the arguments passed during the method call.

Practical Example

class Program
{
    static void Main(string[] args)
    {
        MathOperations math = new MathOperations();
        Console.WriteLine(math.Add(2, 3)); // Output: 5
        Console.WriteLine(math.Add(2.5, 3.5)); // Output: 6.0
        Console.WriteLine(math.Add(1, 2, 3)); // Output: 6
    }
}

Runtime Polymorphism

Method Overriding

Method overriding allows a subclass to provide a specific implementation of a method that is already defined in its superclass.

public class Animal
{
    public virtual void MakeSound()
    {
        Console.WriteLine("Animal makes a sound");
    }
}

public class Dog : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("Dog barks");
    }
}

public class Cat : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("Cat meows");
    }
}

Explanation

  • The MakeSound method in the Animal class is marked as virtual, allowing it to be overridden in derived classes.
  • The Dog and Cat classes override the MakeSound method to provide their specific implementations.

Practical Example

class Program
{
    static void Main(string[] args)
    {
        Animal myDog = new Dog();
        Animal myCat = new Cat();

        myDog.MakeSound(); // Output: Dog barks
        myCat.MakeSound(); // Output: Cat meows
    }
}

Exercises

Exercise 1: Method Overloading

Create a class Calculator with overloaded methods Multiply that can handle:

  • Two integers
  • Two doubles
  • Three integers

Solution:

public class Calculator
{
    public int Multiply(int a, int b)
    {
        return a * b;
    }

    public double Multiply(double a, double b)
    {
        return a * b;
    }

    public int Multiply(int a, int b, int c)
    {
        return a * b * c;
    }
}

class Program
{
    static void Main(string[] args)
    {
        Calculator calc = new Calculator();
        Console.WriteLine(calc.Multiply(2, 3)); // Output: 6
        Console.WriteLine(calc.Multiply(2.5, 3.5)); // Output: 8.75
        Console.WriteLine(calc.Multiply(1, 2, 3)); // Output: 6
    }
}

Exercise 2: Method Overriding

Create a base class Shape with a method Draw. Create derived classes Circle and Rectangle that override the Draw method to print specific messages.

Solution:

public class Shape
{
    public virtual void Draw()
    {
        Console.WriteLine("Drawing a shape");
    }
}

public class Circle : Shape
{
    public override void Draw()
    {
        Console.WriteLine("Drawing a circle");
    }
}

public class Rectangle : Shape
{
    public override void Draw()
    {
        Console.WriteLine("Drawing a rectangle");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Shape myCircle = new Circle();
        Shape myRectangle = new Rectangle();

        myCircle.Draw(); // Output: Drawing a circle
        myRectangle.Draw(); // Output: Drawing a rectangle
    }
}

Common Mistakes and Tips

  • Not using virtual and override keywords correctly: Ensure the base class method is marked with virtual and the derived class method with override.
  • Confusing method overloading with method overriding: Overloading is about different parameter lists, while overriding is about redefining a method in a derived class.
  • Using the wrong method signature: Ensure the method signatures are different for overloading and the same for overriding.

Conclusion

Polymorphism in C# allows for flexible and reusable code. By understanding and applying both compile-time and runtime polymorphism, you can create more dynamic and maintainable applications. In the next topic, we will delve into encapsulation, another fundamental OOP concept.

© Copyright 2024. All rights reserved