C# Inheritance
Introduction
Inheritance is one of the four pillars of Object-Oriented Programming (OOP), alongside encapsulation, abstraction, and polymorphism. In C#, inheritance allows you to create a new class that reuses, extends, and modifies the behavior defined in another class. The class whose members are inherited is called the base class, and the class that inherits those members is called the derived class.
Think of inheritance as a parent-child relationship. Just as a child inherits traits from their parents, a derived class inherits properties and methods from its base class. This powerful concept enables:
- Code reuse: Avoiding duplication by inheriting existing functionality
- Extensibility: Adding new features without modifying existing code
- Specialization: Creating specialized versions of more general classes
- Polymorphism: Treating objects of derived classes as objects of their base class
Basic Syntax
In C#, you use the colon (:
) symbol to establish an inheritance relationship between classes. Here's the basic syntax:
// Base class
class BaseClass
{
// Members (fields, properties, methods, etc.)
}
// Derived class
class DerivedClass : BaseClass
{
// Additional members specific to DerivedClass
}
Simple Inheritance Example
Let's start with a basic example demonstrating inheritance:
using System;
// Base class
class Animal
{
public string Name { get; set; }
public int Age { get; set; }
public void Eat()
{
Console.WriteLine($"{Name} is eating.");
}
public void Sleep()
{
Console.WriteLine($"{Name} is sleeping.");
}
}
// Derived class
class Dog : Animal
{
public void Bark()
{
Console.WriteLine($"{Name} says: Woof! Woof!");
}
public void FetchBall()
{
Console.WriteLine($"{Name} is fetching the ball.");
}
}
class Program
{
static void Main(string[] args)
{
// Create an instance of the derived class
Dog myDog = new Dog();
myDog.Name = "Buddy";
myDog.Age = 3;
// Access methods from the base class
myDog.Eat();
myDog.Sleep();
// Access methods from the derived class
myDog.Bark();
myDog.FetchBall();
}
}
Output:
Buddy is eating.
Buddy is sleeping.
Buddy says: Woof! Woof!
Buddy is fetching the ball.
In this example, Dog
inherits all the public members from Animal
, so a Dog
object can access Name
, Age
, Eat()
, and Sleep()
. It also has its own methods like Bark()
and FetchBall()
.
Constructor Execution in Inheritance
When you create an instance of a derived class, the base class constructor executes first, followed by the derived class constructor. You can explicitly call the base class constructor using the base
keyword:
using System;
class Animal
{
public Animal()
{
Console.WriteLine("Animal constructor called");
}
public Animal(string name)
{
Console.WriteLine($"Animal constructor with name '{name}' called");
}
}
class Dog : Animal
{
public Dog() : base() // Calls the parameterless constructor of Animal
{
Console.WriteLine("Dog constructor called");
}
public Dog(string name) : base(name) // Calls the parameterized constructor of Animal
{
Console.WriteLine($"Dog constructor with name '{name}' called");
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Creating a Dog without parameters:");
Dog dog1 = new Dog();
Console.WriteLine("\nCreating a Dog with name parameter:");
Dog dog2 = new Dog("Rex");
}
}
Output:
Creating a Dog without parameters:
Animal constructor called
Dog constructor called
Creating a Dog with name parameter:
Animal constructor with name 'Rex' called
Dog constructor with name 'Rex' called
Method Overriding
One of the powerful features of inheritance is the ability to override base class methods in the derived class. To enable method overriding:
- Mark the base class method with the
virtual
keyword - Use the
override
keyword in the derived class
using System;
class Animal
{
public string Name { get; set; }
public virtual void MakeSound()
{
Console.WriteLine("The animal makes a sound");
}
}
class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine($"{Name} says: Woof! Woof!");
}
}
class Cat : Animal
{
public override void MakeSound()
{
Console.WriteLine($"{Name} says: Meow! Meow!");
}
}
class Program
{
static void Main(string[] args)
{
Animal myAnimal = new Animal();
myAnimal.Name = "Generic Animal";
myAnimal.MakeSound(); // Calls Animal.MakeSound()
Dog myDog = new Dog();
myDog.Name = "Buddy";
myDog.MakeSound(); // Calls Dog.MakeSound()
Cat myCat = new Cat();
myCat.Name = "Whiskers";
myCat.MakeSound(); // Calls Cat.MakeSound()
// Polymorphism in action
Console.WriteLine("\nPolymorphism Example:");
Animal[] animals = new Animal[3];
animals[0] = new Animal { Name = "Generic Animal" };
animals[1] = new Dog { Name = "Rex" };
animals[2] = new Cat { Name = "Luna" };
foreach (Animal animal in animals)
{
animal.MakeSound(); // Different implementation called based on actual object type
}
}
}
Output:
The animal makes a sound
Buddy says: Woof! Woof!
Whiskers says: Meow! Meow!
Polymorphism Example:
The animal makes a sound
Rex says: Woof! Woof!
Luna says: Meow! Meow!
Notice how each object calls its own implementation of MakeSound()
, even when we're using a reference of type Animal
. This demonstrates polymorphism.
Access Modifiers and Inheritance
Understanding access modifiers is crucial when working with inheritance:
public
members are accessible from anywhere, including derived classesprotected
members are accessible within the class and by derived classesprivate
members are only accessible within the declaring class (not inherited)internal
members are accessible within the same assemblyprotected internal
members are accessible within the same assembly or by derived classes
using System;
class BaseClass
{
public string PublicField = "Public field";
protected string ProtectedField = "Protected field";
private string PrivateField = "Private field";
public void AccessFields()
{
Console.WriteLine("In BaseClass:");
Console.WriteLine($" {PublicField}");
Console.WriteLine($" {ProtectedField}");
Console.WriteLine($" {PrivateField}");
}
}
class DerivedClass : BaseClass
{
public void AccessInheritedFields()
{
Console.WriteLine("In DerivedClass:");
Console.WriteLine($" {PublicField}");
Console.WriteLine($" {ProtectedField}");
// Console.WriteLine($" {PrivateField}"); // Error! PrivateField is not accessible
}
}
class Program
{
static void Main(string[] args)
{
DerivedClass derived = new DerivedClass();
derived.AccessFields();
derived.AccessInheritedFields();
// Access modifiers from outside the class hierarchy
Console.WriteLine("\nIn Main method:");
Console.WriteLine($" {derived.PublicField}");
// Console.WriteLine($" {derived.ProtectedField}"); // Error! ProtectedField is not accessible
// Console.WriteLine($" {derived.PrivateField}"); // Error! PrivateField is not accessible
}
}
Output:
In BaseClass:
Public field
Protected field
Private field
In DerivedClass:
Public field
Protected field
In Main method:
Public field
Sealed Classes and Methods
If you want to prevent a class from being inherited, you can mark it as sealed
:
sealed class FinalClass
{
// This class cannot be inherited
}
class AttemptedDerivedClass : FinalClass // Error! Cannot inherit from sealed class
{
}
Similarly, you can seal individual methods to prevent them from being overridden in further derived classes:
class BaseClass
{
public virtual void VirtualMethod()
{
Console.WriteLine("Base class implementation");
}
}
class DerivedClass : BaseClass
{
public sealed override void VirtualMethod()
{
Console.WriteLine("Derived class implementation");
}
}
class FurtherDerivedClass : DerivedClass
{
// This would cause a compiler error:
// public override void VirtualMethod() { }
// Cannot override sealed method
}
Real-World Example: Shape Hierarchy
Let's implement a more practical example of inheritance by creating a shape class hierarchy:
using System;
// Base class
public abstract class Shape
{
// Properties
public string Color { get; set; }
// Constructor
public Shape(string color)
{
Color = color;
}
// Virtual method
public virtual void DisplayInfo()
{
Console.WriteLine($"This is a {Color} shape.");
}
// Abstract method - must be implemented by derived classes
public abstract double CalculateArea();
}
// Derived class: Circle
public class Circle : Shape
{
// Additional property
public double Radius { get; set; }
// Constructor
public Circle(string color, double radius) : base(color)
{
Radius = radius;
}
// Override DisplayInfo
public override void DisplayInfo()
{
Console.WriteLine($"This is a {Color} circle with radius {Radius}.");
}
// Implement abstract method
public override double CalculateArea()
{
return Math.PI * Radius * Radius;
}
}
// Derived class: Rectangle
public class Rectangle : Shape
{
// Additional properties
public double Width { get; set; }
public double Height { get; set; }
// Constructor
public Rectangle(string color, double width, double height) : base(color)
{
Width = width;
Height = height;
}
// Override DisplayInfo
public override void DisplayInfo()
{
Console.WriteLine($"This is a {Color} rectangle with width {Width} and height {Height}.");
}
// Implement abstract method
public override double CalculateArea()
{
return Width * Height;
}
}
// Another derived class: Triangle
public class Triangle : Shape
{
// Additional properties
public double Base { get; set; }
public double Height { get; set; }
// Constructor
public Triangle(string color, double baseLength, double height) : base(color)
{
Base = baseLength;
Height = height;
}
// Override DisplayInfo
public override void DisplayInfo()
{
Console.WriteLine($"This is a {Color} triangle with base {Base} and height {Height}.");
}
// Implement abstract method
public override double CalculateArea()
{
return 0.5 * Base * Height;
}
}
class Program
{
static void Main(string[] args)
{
// Creating an array of Shape objects
Shape[] shapes = new Shape[3];
shapes[0] = new Circle("red", 5.0);
shapes[1] = new Rectangle("blue", 4.0, 6.0);
shapes[2] = new Triangle("green", 3.0, 8.0);
// Process all shapes polymorphically
foreach (Shape shape in shapes)
{
shape.DisplayInfo();
Console.WriteLine($"Area: {shape.CalculateArea():F2} square units");
Console.WriteLine();
}
}
}
Output:
This is a red circle with radius 5.
Area: 78.54 square units
This is a blue rectangle with width 4 and height 6.
Area: 24.00 square units
This is a green triangle with base 3 and height 8.
Area: 12.00 square units
This example demonstrates several key concepts:
- Abstract classes:
Shape
is abstract, meaning you cannot create an instance of it directly - Abstract methods:
CalculateArea()
is abstract in the base class, forcing derived classes to provide an implementation - Polymorphism: We store different shape objects in a
Shape
array and process them uniformly - Method overriding: Each shape class overrides the
DisplayInfo()
method to provide its own implementation
Multiple Inheritance in C#
Unlike some other languages like C++, C# does not support multiple inheritance for classes. A class can inherit from only one base class. However, C# allows a class to implement multiple interfaces, which provides similar functionality:
using System;
// Interface for flyers
public interface IFlyable
{
void Fly();
}
// Interface for swimmers
public interface ISwimmable
{
void Swim();
}
// Base class
public class Animal
{
public string Name { get; set; }
public void Eat()
{
Console.WriteLine($"{Name} is eating.");
}
}
// Duck class inherits from Animal and implements two interfaces
public class Duck : Animal, IFlyable, ISwimmable
{
public Duck(string name)
{
Name = name;
}
public void Fly()
{
Console.WriteLine($"{Name} is flying.");
}
public void Swim()
{
Console.WriteLine($"{Name} is swimming.");
}
}
class Program
{
static void Main(string[] args)
{
Duck duck = new Duck("Donald");
duck.Eat(); // From Animal base class
duck.Fly(); // From IFlyable interface
duck.Swim(); // From ISwimmable interface
// Demonstrate polymorphism through interfaces
IFlyable flyer = duck;
flyer.Fly();
ISwimmable swimmer = duck;
swimmer.Swim();
}
}
Output:
Donald is eating.
Donald is flying.
Donald is swimming.
Donald is flying.
Donald is swimming.
Summary
Inheritance is a powerful feature of C# and object-oriented programming that allows you to:
- Create class hierarchies where derived classes inherit members from base classes
- Override methods in derived classes to change or extend their behavior
- Create abstract base classes that define a common interface for derived classes
- Implement polymorphism, where a base class reference can refer to objects of derived classes
Some key points to remember:
- Use the
:
syntax to establish inheritance relationships - Use the
virtual
keyword in base classes andoverride
in derived classes for method overriding - Base class constructors are called before derived class constructors
protected
members are accessible in derived classes- C# supports single inheritance for classes but multiple interface implementation
- Use the
sealed
keyword to prevent further inheritance or method overriding - Abstract classes can contain abstract methods that must be implemented by non-abstract derived classes
Practice Exercises
-
Employee Hierarchy: Create a base
Employee
class with properties forName
,ID
, andBaseSalary
. Then create derived classes for different types of employees likeManager
,Developer
, andSalesperson
, each with their own additional properties and methods. -
Vehicle Inheritance: Design a
Vehicle
base class, then create derived classes likeCar
,Motorcycle
, andTruck
. Implement appropriate properties and methods for each, and demonstrate polymorphism. -
Bank Account System: Create an inheritance hierarchy for a banking application with an abstract
Account
class and derived classes likeSavingsAccount
,CheckingAccount
, andInvestmentAccount
. -
Extend the Shape Hierarchy: Add more shapes like
Square
,Ellipse
, andTrapezoid
to the shape hierarchy example from above. -
Mixed Interfaces: Create a complex system combining inheritance and interfaces, like a game with different character types that can have various abilities.
Additional Resources
- Microsoft Documentation on Inheritance
- Microsoft Documentation on Polymorphism
- C# Design Patterns - Many design patterns leverage inheritance
- C# Corner: Abstract Classes vs Interfaces
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)