Inheritance is an object-oriented programming mechanism where a class (derived/child class) acquires properties and behaviors from another class (base/parent class). Polymorphism allows objects of different types to be treated as objects of a common base type, enabling method overriding and dynamic method dispatch.
Common Aliases:
- Inheritance: “IS-A” relationship, class hierarchy, derivation
- Polymorphism: Method overriding, dynamic binding, runtime polymorphism
Abstract Classes
Abstract classes are classes that cannot be instantiated directly and serve as base classes for other classes. They can contain both implemented methods and abstract methods (without implementation) that must be implemented by derived classes.
// Base abstract class defining common structure
public abstract class Shape
{
protected string _name;
// Abstract method - must be implemented by derived classes
public abstract double CalculateArea();
// Concrete method with implementation
public virtual void Display()
{
Console.WriteLine($"Shape: {_name}");
}
// Constructor for base class
protected Shape(string name)
{
_name = name;
}
}
// Derived class implementing the abstract method
public class Circle : Shape
{
public double Radius { get; set; }
public Circle(string name, double radius) : base(name)
{
Radius = radius;
}
// Mandatory implementation of abstract method
public override double CalculateArea()
{
return Math.PI * Radius * Radius;
}
// Optional override of virtual method
public override void Display()
{
Console.WriteLine($"Circle '{_name}' with radius {Radius}");
}
}
Abstract classes provide a template for derived classes while allowing partial implementation.
Virtual Methods
Virtual methods are methods in a base class that can be overridden in derived classes using the override keyword. They enable runtime polymorphism by allowing the most derived implementation to be called.
public class Vehicle
{
public string Model { get; set; }
// Virtual method - can be overridden by derived classes
public virtual void StartEngine()
{
Console.WriteLine($"Starting {Model} engine with key ignition");
}
// Non-virtual method - cannot be overridden (only hidden with 'new')
public void StopEngine()
{
Console.WriteLine("Engine stopped");
}
}
public class ElectricCar : Vehicle
{
// Overriding virtual method with specialized implementation
public override void StartEngine()
{
Console.WriteLine($"Starting {Model} with silent electric power");
}
// Hiding base method (not recommended - use virtual/override pattern)
public new void StopEngine()
{
Console.WriteLine("Electric motor disengaged");
}
}
// Usage demonstrating polymorphism
Vehicle myCar = new ElectricCar { Model = "Tesla Model S" };
myCar.StartEngine(); // Calls ElectricCar's implementation due to polymorphism
Method Hiding vs. Overriding: Use override for polymorphism. The new keyword hides the base method but doesn’t provide true polymorphic behavior.
Interface Implementation
Interfaces define contracts that classes must implement. Unlike abstract classes, interfaces contain only method signatures without implementations (prior to C# 8.0). A class can implement multiple interfaces.
// Interface defining a contract
public interface ILoggable
{
void Log(string message);
string GetLogHeader();
}
public interface IIdentifiable
{
Guid Id { get; }
}
// Class implementing multiple interfaces
public class DatabaseService : ILoggable, IIdentifiable
{
public Guid Id { get; } = Guid.NewGuid();
private List<string> _logEntries = new List<string>();
// Explicit interface implementation
void ILoggable.Log(string message)
{
_logEntries.Add($"{DateTime.Now}: {message}");
}
string ILoggable.GetLogHeader()
{
return $"Database Service {Id} - Log entries: {_logEntries.Count}";
}
// Regular class method
public void ClearLogs()
{
_logEntries.Clear();
}
}
// Usage with interface-based polymorphism
ILoggable logger = new DatabaseService();
logger.Log("Database connection established");
Console.WriteLine(logger.GetLogHeader());
Multiple Inheritance: Interfaces enable a form of multiple inheritance in C#, as a class can implement multiple interfaces.
Why Inheritance and Polymorphism Matter
- DRY Principle: Inheritance eliminates code duplication by allowing common functionality to be defined once
- Open/Closed Principle (SOLID): Polymorphism enables systems to be open for extension but closed for modification
- Liskov Substitution Principle (SOLID): Proper inheritance ensures derived classes can substitute base classes
Advanced Nuances
Covariant Return Types (C# 9.0+)
public abstract class Animal
{
public abstract Animal Clone();
}
public class Dog : Animal
{
// Covariant return type - can return more derived type
public override Dog Clone()
{
return new Dog();
}
}
Explicit Interface Implementation for Diamond Problem
interface IA { void Method(); }
interface IB { void Method(); }
class Diamond : IA, IB
{
// Explicit implementations resolve method ambiguity
void IA.Method() => Console.WriteLine("IA implementation");
void IB.Method() => Console.WriteLine("IB implementation");
}
Protected Internal Access Modifier
public class BaseClass
{
// Accessible within same assembly OR by derived classes in any assembly
protected internal string HybridAccessField;
}
Roadmap Context
In the “Object Oriented Programming” section, Inheritance and Polymorphism serve as the foundation for more advanced concepts:
- Design Patterns: Factory, Strategy, Template Method patterns
- Dependency Injection: Modern DI containers use polymorphism extensively
- Entity Framework & ORM: Navigation properties and inheritance mapping
- ASP.NET Core Middleware: Pipeline pattern using polymorphism