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)}");