Part 7: Inheritance, Polymorphism, and Interfaces in C#

Inheritance, Polymorphism, and Interfaces are foundational concepts in Object-Oriented Programming (OOP) that enable the creation of scalable, modular, and reusable code. Understanding and applying these principles effectively is essential for building robust software systems. This guide explores these concepts in depth, with practical examples and scenarios where they are particularly useful.

1. Why Are These Concepts Important?

Scalability and Modularity

  • Inheritance allows code reuse by enabling new classes to build upon existing ones.
  • Polymorphism simplifies extensibility by allowing methods to behave differently based on the object type.
  • Interfaces define contracts that ensure consistency while allowing flexibility in implementation.

When to Choose Inheritance vs Interfaces

  • Use inheritance when you want to establish an "is-a" relationship (e.g., a Dog "is a" type of Animal).
  • Use interfaces when you want to define capabilities or behaviours that multiple unrelated classes can share (e.g., a Bird and an Airplane can both implement IFlyable).

2. Inheritance

Inheritance allows a class (subclass) to inherit properties and methods from another class (superclass), promoting code reuse and hierarchy creation.

Practical Example of Inheritance

public class Animal
{
    public string Name { get; set; }

    public void Eat()
    {
        Console.WriteLine($"{Name} is eating.");
    }
}

public class Dog : Animal
{
    public void Bark()
    {
        Console.WriteLine($"{Name} is barking.");
    }
}

// Using inheritance
Dog myDog = new Dog();
myDog.Name = "Buddy";
myDog.Eat();
myDog.Bark();

3. Polymorphism

Polymorphism allows methods to have different behaviours depending on the object type. This can be achieved through method overriding or interfaces.

Example of Polymorphism with Method Overriding

public class Bird
{
    public virtual void Fly()
    {
        Console.WriteLine("The bird is flying.");
    }
}

public class Eagle : Bird
{
    public override void Fly()
    {
        Console.WriteLine("The eagle soars at great heights.");
    }
}

// Using polymorphism
Bird myBird = new Eagle();
myBird.Fly(); // Output: The eagle soars at great heights.

4. Interfaces

An interface defines a contract that classes must adhere to, ensuring consistency while allowing flexibility in implementation.

Example of Interface Implementation

public interface IFlyable
{
    void Fly();
}

public class Airplane : IFlyable
{
    public void Fly()
    {
        Console.WriteLine("The airplane is flying.");
    }
}

public class Bird : IFlyable
{
    public void Fly()
    {
        Console.WriteLine("The bird is flying.");
    }
}

// Using interfaces
IFlyable airplane = new Airplane();
IFlyable bird = new Bird();

airplane.Fly();
bird.Fly();

Handling Errors with Interfaces

Adding validations can make implementations more robust:

public class BankAccount
{
    private decimal balance;

    public void Deposit(decimal amount)
    {
        if (amount <= 0)
        {
            Console.WriteLine("Deposit amount must be greater than zero.");
            return;
        }
        balance += amount;
        Console.WriteLine($"Deposit successful. New balance: {balance}");
    }
}

5. Combined Example: Inheritance, Polymorphism, and Interfaces

Let’s create a practical example that combines all three concepts:

public interface IDriveable
{
    void Drive();
}

public class Vehicle
{
    public string Make { get; set; }
    public string Model { get; set; }

    public virtual void DisplayInfo()
    {
        Console.WriteLine($"Vehicle: {Make} {Model}");
    }
}

public class Car : Vehicle, IDriveable
{
    public override void DisplayInfo()
    {
        Console.WriteLine($"Car: {Make} {Model}");
    }

    public void Drive()
    {
        Console.WriteLine("The car is driving.");
    }
}

public class Motorcycle : Vehicle, IDriveable
{
    public override void DisplayInfo()
    {
        Console.WriteLine($"Motorcycle: {Make} {Model}");
    }

    public void Drive()
    {
        Console.WriteLine("The motorcycle is driving.");
    }
}

// Using the combined example
IDriveable car = new Car { Make = "Toyota", Model = "Corolla" };
IDriveable motorcycle = new Motorcycle { Make = "Harley-Davidson", Model = "Sportster" };

car.Drive();
motorcycle.Drive();

6. Advanced Concepts and Patterns

Patterns Related to These Concepts

  • Strategy Pattern: Use interfaces to switch between algorithms dynamically.
  • Template Method Pattern: Use inheritance to define a common structure with specific behaviours implemented by subclasses.

Example of the Strategy Pattern

public interface IShippingStrategy
{
    decimal CalculateShipping(decimal weight);
}

public class StandardShipping : IShippingStrategy
{
    public decimal CalculateShipping(decimal weight) => weight * 1.5m;
}

public class ExpressShipping : IShippingStrategy
{
    public decimal CalculateShipping(decimal weight) => weight * 3.0m;
}
}

// Usage
IShippingStrategy strategy = new ExpressShipping();
Console.WriteLine($"Shipping cost: {strategy.CalculateShipping(10)}");
TipsCsharpDotnetBeginners
Avatar for Adrián Bailador

Written by Adrián Bailador

🚀 Full-Stack Dev 👨🏻‍💻 .NET Engineer 👾 Geek & Friki 💡 Talks about #dotnet, #csharp, #azure, #visualstudio and a little bit of #nextjs.

Loading

Fetching comments

Hey! 👋

Got something to say?

or to leave a comment.