Inheritance
Explore inheritance in C# to understand how derived classes reuse and extend base class properties and methods. This lesson covers single inheritance, the is-a relationship, casting, the Object base class, sealed classes, and how composition differs from inheritance, helping you write flexible and maintainable object-oriented code.
Inheritance is a fundamental concept of object-oriented programming. With inheritance, one class can inherit the functionality of another class. This allows us to encapsulate common functionality in a base class and implement specific functionality in derived classes.
The best way to understand inheritance in C# is to see it in action.
Let’s take a Car class as an example:
public class Car{public string Model { get; set; }public decimal Price { get; set; }public int Year { get; set; }public int FuelCapacityInLiters { get; set; }public int FuelLeft { get; set; }public void Drive(){}}
Lines 3–7: We define properties representing the state of the car, such as model, price, and fuel capacity.
Lines 9–11: We define a
Drivemethod representing the car’s behavior.
Our car has a model and price, along with other properties, and can be driven. What if we want to create a Motorcycle class? It also has a price and model and can be driven.
Should we merely copy the code from the Car class to the Motorcycle class?
A better approach is to use inheritance. We can identify properties and methods applicable to both Car and Motorcycle and move them to a base class. We can define this common functionality within a new Vehicle class.
Implementing inheritance
Because the Car and Motorcycle classes have some functionality in common, we create a Vehicle class to hold those common properties and methods.
Lines 5–10: We define the shared properties that apply to all vehicles.
Lines 12–14: We define the
Drivemethod, which is common to both cars and motorcycles.
When we want to inherit from another type, we use the : operator after the class name:
Line 6: We use the
:syntax to establish thatCarinherits fromVehicle.Lines 7–8: The class body is currently empty, but it automatically acquires all members of
Vehicle.
Similarly, we can define the Motorcycle class to inherit from the same base class:
Line 4: We establish that
Motorcycleis a derived class ofVehicle.
Inheritance introduces an is-a relationship. A car is a vehicle, and so is a motorcycle.
The diagram above shows that both Car and Motorcycle inherit from the Vehicle class. Because the Car inherits from Vehicle, it has all Vehicle properties, fields, and methods. Note that constructors are not inherited; derived classes must define their own.
Using inherited members
We can create an instance of Car and access the properties defined in Vehicle directly:
Line 1: We import the
Inheritancenamespace.Lines 4–6: We instantiate
Carand set its properties directly.Lines 11–13: We instantiate
Motorcycleand set its properties.Lines 8 & 15: We print the results to the console.
We can assign a Car object to a Vehicle variable because of the is-a relationship. Since all cars are vehicles, a Vehicle variable can store a reference to any Car object.
Vehicle myCar = new Car();
Implicit casting works because Car derives from Vehicle. However, because not all vehicles are cars, we cannot hold a Vehicle object inside a Car variable without explicit casting.
Inheritance from the Object class
All types in .NET implicitly derive from the base Object class:
// Derives from Objectpublic class SomeClass{}
Line 2: Even without a
:,SomeClassinherits fromSystem.Object.
Consequently, every type in .NET inherits four methods from the Object class:
ToString()Equals()GetHashCode()GetType()
An instance of the Car class also has these methods because its base type (Vehicle) inherits from Object:
Line 7: Calls
ToString(), which returns the fully qualified name of the type by default.Line 8: Calls
Equals()to compare references (unless overridden).Line 9: Calls
GetHashCode()to retrieve a numeric hash code for the object.Line 10: Calls
GetType()to get the runtime type of the instance.
Considering that all types derive from System.Object, we can visualize the updated inheritance hierarchy:
Constraints
Any class can inherit from another class, but there are some constraints to consider:
Single inheritance: A class can inherit from only one class. Multiple inheritance is not supported in .NET.
Access modifiers: A derived class cannot be more accessible than its base class. For example, we cannot declare a
publicclass that inherits from aninternalclass.Sealed classes: When you want to stop the inheritance chain, you use a specific modifier. If you are curious about what are sealed classes in C#, they are classes that cannot be inherited from. By marking a class as
sealed, you ensure that no other developer can create a derived version of it.
Line 1: The
sealedkeyword prevents other classes from inheriting fromSomeClass.Line 5: This declaration generates a compiler error. Static classes cannot be inherited.
Note: The common language runtime (CLR) prohibits multiple class inheritance, so C# does not support it.
Inheritance vs. composition
Inheritance provides a powerful way to reuse code. However, it is not always the best tool for every scenario. Software design relies heavily on two primary mechanisms for code reuse: inheritance and composition.
Inheritance models an is-a relationship. A car is a vehicle. A motorcycle is a vehicle.
Composition models a has-a relationship. Instead of inheriting from a class, a class contains an instance of another class. A car has an engine. A car has wheels.
namespace Inheritance;public class Engine{public void Start(){Console.WriteLine("Engine started.");}}// Composition: Car "has-a" Enginepublic class Car : Vehicle{public Engine CarEngine { get; set; } = new Engine();public int NumberOfDoors { get; set; }}
Lines 3–9: We define an independent
Engineclass.Line 14: We compose the
Carclass by giving it anEngineproperty. TheCarrelies onEnginefor starting logic without inheriting from it.
Modern C# development generally favors composition over inheritance. Deep inheritance hierarchies become rigid and difficult to maintain. Because C# restricts classes to single inheritance, tying a class to a base class limits future flexibility. Composition allows developers to assemble complex behaviors from small, independent components.