C# Inheritance
Overview
Inheritance is a fundamental concept in object-oriented programming that allows a class to inherit properties and methods from another class. It promotes code reuse and establishes "is-a" relationships between classes.
Basic Inheritance
Base and Derived Classes
csharp
// Base class (parent)
public class Vehicle
{
public string Brand { get; set; }
public string Model { get; set; }
public int Year { get; set; }
public int Speed { get; protected set; }
public Vehicle(string brand, string model, int year)
{
Brand = brand;
Model = model;
Year = year;
Speed = 0;
}
public virtual void Accelerate(int amount)
{
Speed += amount;
Console.WriteLine($"{Brand} {Model} accelerated to {Speed} km/h");
}
public virtual void Brake(int amount)
{
Speed = Math.Max(0, Speed - amount);
Console.WriteLine($"{Brand} {Model} slowed to {Speed} km/h");
}
public virtual void DisplayInfo()
{
Console.WriteLine($"Vehicle: {Brand} {Model} ({Year})");
}
}
// Derived class (child)
public class Car : Vehicle
{
public int NumberOfDoors { get; set; }
public string FuelType { get; set; }
public Car(string brand, string model, int year, int doors, string fuel)
: base(brand, model, year)
{
NumberOfDoors = doors;
FuelType = fuel;
}
public override void DisplayInfo()
{
Console.WriteLine($"Car: {Brand} {Model} ({Year}) - {NumberOfDoors} doors, {FuelType}");
}
public void OpenTrunk()
{
Console.WriteLine("Opening car trunk...");
}
}Constructor Chaining
csharp
public class ElectricCar : Car
{
public int BatteryCapacity { get; set; }
public int Range { get; set; }
// Constructor that calls base constructor
public ElectricCar(string brand, string model, int year, int doors,
int batteryCapacity, int range)
: base(brand, model, year, doors, "Electric")
{
BatteryCapacity = batteryCapacity;
Range = range;
}
// Constructor with default values
public ElectricCar(string brand, string model, int year)
: this(brand, model, year, 4, 75, 300)
{
}
public override void DisplayInfo()
{
base.DisplayInfo();
Console.WriteLine($"Battery: {BatteryCapacity}kWh, Range: {Range}km");
}
public void Charge()
{
Console.WriteLine("Charging electric car...");
}
}Method Overriding
Virtual and Override Methods
csharp
public class Animal
{
public string Name { get; set; }
public Animal(string name)
{
Name = name;
}
public virtual void MakeSound()
{
Console.WriteLine($"{Name} makes a generic animal sound");
}
public virtual void Move()
{
Console.WriteLine($"{Name} moves in some way");
}
}
public class Dog : Animal
{
public string Breed { get; set; }
public Dog(string name, string breed) : base(name)
{
Breed = breed;
}
public override void MakeSound()
{
Console.WriteLine($"{Name} the {Breed} barks: Woof! Woof!");
}
public override void Move()
{
Console.WriteLine($"{Name} the {Breed} runs happily");
}
public void WagTail()
{
Console.WriteLine($"{Name} wags tail excitedly");
}
}
public class Cat : Animal
{
public string Color { get; set; }
public Cat(string name, string color) : base(name)
{
Color = color;
}
public override void MakeSound()
{
Console.WriteLine($"{Name} the {Color} cat meows: Meow!");
}
public override void Move()
{
Console.WriteLine($"{Name} the {Color} cat sneaks silently");
}
public void Purr()
{
Console.WriteLine($"{Name} purrs contentedly");
}
}Calling Base Methods
csharp
public class Bird : Animal
{
public double WingSpan { get; set; }
public Bird(string name, double wingSpan) : base(name)
{
WingSpan = wingSpan;
}
public override void MakeSound()
{
Console.WriteLine($"{Name} chirps: Tweet! Tweet!");
}
public override void Move()
{
base.Move(); // Call base implementation
Console.WriteLine($"{Name} flies with {WingSpan}m wingspan");
}
public void Fly()
{
Console.WriteLine($"{Name} soars through the sky");
}
}Abstract Classes
Abstract Class Definition
csharp
public abstract class Shape
{
public string Name { get; set; }
public double Width { get; set; }
public double Height { get; set; }
protected Shape(string name, double width, double height)
{
Name = name;
Width = width;
Height = height;
}
// Abstract method - must be implemented by derived classes
public abstract double CalculateArea();
// Virtual method - can be overridden
public virtual void DisplayInfo()
{
Console.WriteLine($"Shape: {Name} ({Width} x {Height})");
}
// Concrete method - inherited as-is
public double GetPerimeter()
{
return 2 * (Width + Height);
}
}Implementing Abstract Classes
csharp
public class Rectangle : Shape
{
public Rectangle(double width, double height) : base("Rectangle", width, height)
{
}
public override double CalculateArea()
{
return Width * Height;
}
public override void DisplayInfo()
{
base.DisplayInfo();
Console.WriteLine($"Area: {CalculateArea():F2}");
}
}
public class Circle : Shape
{
public double Radius { get; set; }
public Circle(double radius) : base("Circle", radius * 2, radius * 2)
{
Radius = radius;
}
public override double CalculateArea()
{
return Math.PI * Radius * Radius;
}
public override void DisplayInfo()
{
Console.WriteLine($"Shape: Circle (Radius: {Radius})");
Console.WriteLine($"Area: {CalculateArea():F2}");
}
}
public class Triangle : Shape
{
public double SideA { get; set; }
public double SideB { get; set; }
public double SideC { get; set; }
public Triangle(double sideA, double sideB, double sideC)
: base("Triangle", sideA, sideB)
{
SideA = sideA;
SideB = sideB;
SideC = sideC;
}
public override double CalculateArea()
{
// Heron's formula
double s = (SideA + SideB + SideC) / 2;
return Math.Sqrt(s * (s - SideA) * (s - SideB) * (s - SideC));
}
public override void DisplayInfo()
{
Console.WriteLine($"Shape: Triangle ({SideA}, {SideB}, {SideC})");
Console.WriteLine($"Area: {CalculateArea():F2}");
}
}Sealed Classes and Methods
Sealed Classes
csharp
// Sealed class cannot be inherited
public sealed class FinalClass
{
public void DoSomething()
{
Console.WriteLine("This class cannot be inherited");
}
}
// This would cause compile error:
// public class DerivedFromFinal : FinalClass { }
// Sealed method cannot be overridden
public class BaseWithSealedMethod
{
public virtual void NormalMethod()
{
Console.WriteLine("This can be overridden");
}
public sealed void SealedMethod()
{
Console.WriteLine("This cannot be overridden");
}
}
public class DerivedFromSealed : BaseWithSealedMethod
{
public override void NormalMethod()
{
Console.WriteLine("Overridden normal method");
}
// This would cause compile error:
// public override void SealedMethod() { }
}Interfaces and Multiple Inheritance
Interface Definition
csharp
public interface IDrawable
{
void Draw();
double GetArea();
}
public interface IMovable
{
void Move(double deltaX, double deltaY);
}
public interface IResizable
{
void Resize(double factor);
}Implementing Multiple Interfaces
csharp
public class DrawableShape : Shape, IDrawable, IMovable, IResizable
{
public DrawableShape(string name, double width, double height)
: base(name, width, height)
{
}
// Implement IDrawable
public void Draw()
{
Console.WriteLine($"Drawing {Name} at position (0, 0)");
}
public double GetArea()
{
return CalculateArea();
}
// Implement IMovable
public void Move(double deltaX, double deltaY)
{
Console.WriteLine($"Moving {Name} by ({deltaX}, {deltaY})");
}
// Implement IResizable
public void Resize(double factor)
{
Width *= factor;
Height *= factor;
Console.WriteLine($"Resized {Name} by factor {factor}");
}
}Advanced Inheritance Concepts
Protected Internal Access
csharp
public class BaseClass
{
protected internal string ProtectedInternalData { get; set; }
protected internal void ProtectedInternalMethod()
{
Console.WriteLine("Protected internal method called");
}
}
// Accessible in same assembly or derived classes
public class DerivedInSameAssembly : BaseClass
{
public void AccessProtectedInternal()
{
ProtectedInternalData = "Accessible";
ProtectedInternalMethod();
}
}Base Class Access
csharp
public class Derived : Base
{
public void CallBaseMethods()
{
// Call base constructor
// base(args);
// Call base method
base.SomeMethod();
// Call base property
string value = base.SomeProperty;
// Call base overridden method
base.OverriddenMethod();
}
public override void OverriddenMethod()
{
// Call base implementation
base.OverriddenMethod();
// Add derived functionality
Console.WriteLine("Derived additional functionality");
}
}Constructor Order
csharp
public class ConstructorOrderDemo
{
public ConstructorOrderDemo()
{
Console.WriteLine("Instance constructor");
}
static ConstructorOrderDemo()
{
Console.WriteLine("Static constructor");
}
}
public class DerivedOrder : ConstructorOrderDemo
{
public DerivedOrder() : base()
{
Console.WriteLine("Derived constructor");
}
static DerivedOrder()
{
Console.WriteLine("Derived static constructor");
}
}
// Execution order:
// 1. Base static constructor
// 2. Derived static constructor
// 3. Base instance constructor
// 4. Derived instance constructorPractical Examples
Employee Hierarchy
csharp
public abstract class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public decimal BaseSalary { get; set; }
public DateTime HireDate { get; set; }
protected Employee(int id, string name, decimal baseSalary)
{
Id = id;
Name = name;
BaseSalary = baseSalary;
HireDate = DateTime.Now;
}
public abstract decimal CalculateSalary();
public virtual void DisplayInfo()
{
Console.WriteLine($"Employee: {Name} (ID: {Id})");
Console.WriteLine($"Hired: {HireDate:yyyy-MM-dd}");
Console.WriteLine($"Base Salary: ${BaseSalary:N2}");
}
}
public class FullTimeEmployee : Employee
{
public decimal Bonus { get; set; }
public FullTimeEmployee(int id, string name, decimal baseSalary, decimal bonus)
: base(id, name, baseSalary)
{
Bonus = bonus;
}
public override decimal CalculateSalary()
{
return BaseSalary + Bonus;
}
public override void DisplayInfo()
{
base.DisplayInfo();
Console.WriteLine($"Bonus: ${Bonus:N2}");
Console.WriteLine($"Total Salary: ${CalculateSalary():N2}");
}
}
public class PartTimeEmployee : Employee
{
public decimal HourlyRate { get; set; }
public int HoursWorked { get; set; }
public PartTimeEmployee(int id, string name, decimal hourlyRate)
: base(id, name, 0)
{
HourlyRate = hourlyRate;
}
public override decimal CalculateSalary()
{
return HourlyRate * HoursWorked;
}
public override void DisplayInfo()
{
base.DisplayInfo();
Console.WriteLine($"Hourly Rate: ${HourlyRate:N2}");
Console.WriteLine($"Hours Worked: {HoursWorked}");
Console.WriteLine($"Total Salary: ${CalculateSalary():N2}");
}
}
public class Contractor : Employee
{
public decimal ContractAmount { get; set; }
public int ContractMonths { get; set; }
public Contractor(int id, string name, decimal contractAmount, int contractMonths)
: base(id, name, 0)
{
ContractAmount = contractAmount;
ContractMonths = contractMonths;
}
public override decimal CalculateSalary()
{
return ContractAmount / ContractMonths;
}
public override void DisplayInfo()
{
base.DisplayInfo();
Console.WriteLine($"Contract Amount: ${ContractAmount:N2}");
Console.WriteLine($"Contract Months: {ContractMonths}");
Console.WriteLine($"Monthly Salary: ${CalculateSalary():N2}");
}
}Shape Hierarchy
csharp
public abstract class Shape
{
public string Name { get; set; }
public double X { get; set; }
public double Y { get; set; }
protected Shape(string name, double x, double y)
{
Name = name;
X = x;
Y = y;
}
public abstract double CalculateArea();
public virtual void Move(double deltaX, double deltaY)
{
X += deltaX;
Y += deltaY;
Console.WriteLine($"{Name} moved to ({X}, {Y})");
}
public virtual void DisplayInfo()
{
Console.WriteLine($"{Name} at ({X}, {Y}) - Area: {CalculateArea():F2}");
}
}
public class Circle : Shape
{
public double Radius { get; set; }
public Circle(double x, double y, double radius) : base("Circle", x, y)
{
Radius = radius;
}
public override double CalculateArea()
{
return Math.PI * Radius * Radius;
}
public override void DisplayInfo()
{
Console.WriteLine($"Circle at ({X}, {Y}) with radius {Radius}");
base.DisplayInfo();
}
}
public class Rectangle : Shape
{
public double Width { get; set; }
public double Height { get; set; }
public Rectangle(double x, double y, double width, double height)
: base("Rectangle", x, y)
{
Width = width;
Height = height;
}
public override double CalculateArea()
{
return Width * Height;
}
public override void DisplayInfo()
{
Console.WriteLine($"Rectangle at ({X}, {Y}) with size {Width}x{Height}");
base.DisplayInfo();
}
}Best Practices
Inheritance Design Guidelines
csharp
// Good: Use inheritance for "is-a" relationships
public class Vehicle { }
public class Car : Vehicle { } // Car is a Vehicle
// Bad: Don't use inheritance for "has-a" relationships
public class Car : Engine { } // Car has an Engine, not is an Engine
// Good: Use composition instead
public class Car
{
private Engine _engine; // Composition
public Car(Engine engine) { _engine = engine; }
}Liskov Substitution Principle
csharp
// Good: Derived classes can substitute base classes
public class Rectangle
{
public virtual void SetWidth(double width) { }
public virtual void SetHeight(double height) { }
}
public class Square : Rectangle
{
public override void SetWidth(double width)
{
base.SetWidth(width);
base.SetHeight(width); // Maintain square property
}
public override void SetHeight(double height)
{
base.SetWidth(height);
base.SetHeight(height); // Maintain square property
}
}Abstract vs Interface
csharp
// Use abstract classes when:
// - You want to provide some default implementation
// - You need to share code among derived classes
public abstract class Animal
{
protected string Name { get; set; }
public Animal(string name) { Name = name; }
public virtual void MakeSound() { /* Default implementation */ }
public abstract void Move(); // Must be implemented
}
// Use interfaces when:
// - You want to define a contract
// - Multiple inheritance is needed
public interface IFlyable
{
void Fly();
void Land();
}Summary
In this chapter, you learned:
- Basic inheritance concepts and syntax
- Constructor chaining and execution order
- Method overriding with virtual and override
- Abstract classes and methods
- Sealed classes and methods
- Interface implementation for multiple inheritance
- Advanced inheritance concepts
- Practical examples and best practices
Inheritance is a powerful tool for creating hierarchical class relationships and promoting code reuse. When used properly, it creates maintainable and extensible code structures. In the next chapter, we'll explore polymorphism to create more flexible and dynamic object behaviors.