Search⌘ K
AI Features

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 Drive method 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.

C# 14.0
namespace Inheritance
{
public class Vehicle
{
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 short NumberOfWheels { get; set; }
public void Drive()
{
}
}
}
  • Lines 5–10: We define the shared properties that apply to all vehicles.

  • Lines 12–14: We define the Drive method, which is common to both cars and motorcycles.

When we want to inherit from another type, we use the : operator after the class name:

C# 14.0
namespace Inheritance
{
// Car inherits from Vehicle
// Vehicle is a base class for Car
public class Car : Vehicle
{
}
}
  • Line 6: We use the : syntax to establish that Car inherits from Vehicle.

  • 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:

C# 14.0
namespace Inheritance
{
public class Motorcycle : Vehicle
{
}
}
  • Line 4: We establish that Motorcycle is a derived class of Vehicle.

Inheritance introduces an is-a relationship. A car is a vehicle, and so is a motorcycle.

Class diagram depicting inheritance
Class diagram depicting inheritance

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:

C# 14.0
using Inheritance;
// Create a Car instance
Car myCar = new Car();
myCar.Model = "Toyota Prius";
myCar.Price = 12000;
Console.WriteLine($"{myCar.Model} costs {myCar.Price} dollars.");
// Create a Motorcycle instance
Motorcycle myBike = new Motorcycle();
myBike.Model = "Harley Davidson";
myBike.Price = 20000;
Console.WriteLine($"{myBike.Model} costs {myBike.Price} dollars.");
  • Line 1: We import the Inheritance namespace.

  • Lines 4–6: We instantiate Car and set its properties directly.

  • Lines 11–13: We instantiate Motorcycle and 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 Object
public class SomeClass
{
}
Implicit inheritance from System.Object
  • Line 2: Even without a :, SomeClass inherits from System.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:

C# 14.0
using Inheritance;
Car myCar = new Car();
Car anotherCar = new Car();
// Calling the methods of the Object class
Console.WriteLine(myCar.ToString());
Console.WriteLine(myCar.Equals(anotherCar));
Console.WriteLine(myCar.GetHashCode());
Console.WriteLine(myCar.GetType());
  • 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:

Inheriting Object’s methods
Inheriting Object’s methods

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 public class that inherits from an internal class.

  • 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.

C# 14.0
public sealed class SomeClass
{
}
public class MyClass : SomeClass // Error: cannot derive from sealed type 'SomeClass'
{
}
  • Line 1: The sealed keyword prevents other classes from inheriting from SomeClass.

  • 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" Engine
public class Car : Vehicle
{
public Engine CarEngine { get; set; } = new Engine();
public int NumberOfDoors { get; set; }
}
Using composition to include functionality
  • Lines 3–9: We define an independent Engine class.

  • Line 14: We compose the Car class by giving it an Engine property. The Car relies on Engine for 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.