C# inheritance
last modified October 16, 2026
In this article we show how to use inheritance in C#.
Inheritance is a way to form new classes using classes that have already been defined. The new class is called a derived class (or a subclass); the class it inherits from is called a base class (or a superclass). The derived class inherits the members of the base class: fields, properties, methods, and events. Constructors and finalizers are not inherited.
Inheritance models the is-a relationship. A dog is an animal; therefore,
a Dog class can derive from an Animal class. The main
benefits of inheritance are code reuse and the ability to treat objects of
different derived classes through a common base type, which is the foundation of
polymorphism.
C# supports only single inheritance of classes; a class can have exactly one
direct base class. However, a class can implement any number of
interfaces. All types in C# ultimately derive
from System.Object.
C# simple inheritance
A derived class is declared with a colon followed by the name of the base class.
var dog = new Dog();
dog.Name = "Rex";
dog.Eat();
dog.Bark();
class Animal
{
public string Name { get; set; } = "";
public void Eat()
{
Console.WriteLine($"{Name} is eating");
}
}
class Dog : Animal
{
public void Bark()
{
Console.WriteLine($"{Name} is barking");
}
}
We have a base Animal class and a derived Dog class.
class Dog : Animal
The Dog class inherits from the Animal class.
dog.Name = "Rex"; dog.Eat(); dog.Bark();
The Name property and the Eat method are inherited
from the base class; the Bark method is defined in the
Dog class itself.
$ dotnet run Rex is eating Rex is barking
C# base constructor
When a derived object is created, the base class constructor is called first.
The base keyword is used to pass arguments to the constructor of
the base class.
var emp = new Employee("John Doe", 34, "programmer");
Console.WriteLine(emp.Info());
class Person
{
public string Name { get; }
public int Age { get; }
public Person(string name, int age)
{
Console.WriteLine("Person constructor");
Name = name;
Age = age;
}
}
class Employee : Person
{
public string Occupation { get; }
public Employee(string name, int age, string occupation) : base(name, age)
{
Console.WriteLine("Employee constructor");
Occupation = occupation;
}
public string Info() => $"{Name}, {Age}, {Occupation}";
}
The Person class has no parameterless constructor; therefore, the
Employee constructor must call the base constructor explicitly.
public Employee(string name, int age, string occupation) : base(name, age)
The base(name, age) initializer calls the constructor of the
Person class before the body of the Employee
constructor is executed.
$ dotnet run Person constructor Employee constructor John Doe, 34, programmer
The output shows the order in which the constructors are called.
C# protected members
Private members of the base class are not accessible in the derived class. The
protected access modifier makes a member accessible in the class
itself and in all derived classes, but not from outside code.
var acc = new SavingsAccount(1000m, 0.03m);
acc.AddInterest();
Console.WriteLine(acc.Balance);
class Account
{
protected decimal balance;
public Account(decimal initial)
{
balance = initial;
}
public decimal Balance => balance;
}
class SavingsAccount : Account
{
private readonly decimal rate;
public SavingsAccount(decimal initial, decimal rate) : base(initial)
{
this.rate = rate;
}
public void AddInterest()
{
balance += balance * rate;
}
}
The SavingsAccount class modifies the protected
balance field of its base class.
protected decimal balance;
The field can be modified in derived classes. From the outside code, the
balance can only be read via the Balance property.
$ dotnet run 1030.00
C# overriding methods
A derived class can provide its own implementation of a method that is
declared as virtual in the base class. The new implementation is
marked with the override modifier. Inside the overriding method, we
can call the original implementation with base.
var shapes = new Shape[] { new Shape(), new Circle(2.0) };
foreach (var shape in shapes)
{
Console.WriteLine(shape.Describe());
}
class Shape
{
public virtual string Describe() => "a shape";
}
class Circle : Shape
{
private readonly double radius;
public Circle(double radius)
{
this.radius = radius;
}
public override string Describe() =>
$"{base.Describe()}: circle with area {Math.PI * radius * radius:F2}";
}
The Circle class overrides the Describe method and
extends the text returned by the base implementation.
public virtual string Describe() => "a shape";
Only methods marked as virtual, abstract, or
override can be overridden.
$ dotnet run a shape a shape: circle with area 12.57
Even though the array has the Shape element type, the overridden
method of the Circle class is called for the second element. This
is polymorphism.
C# multilevel inheritance
A derived class can serve as a base class for another class. This creates an inheritance hierarchy.
var puppy = new Puppy();
Console.WriteLine(puppy is Dog);
Console.WriteLine(puppy is Animal);
Console.WriteLine(puppy is object);
Type? t = typeof(Puppy);
while (t != null)
{
Console.WriteLine(t.Name);
t = t.BaseType;
}
class Animal { }
class Dog : Animal { }
class Puppy : Dog { }
A Puppy is a Dog, which is an Animal.
Console.WriteLine(puppy is Animal);
The is operator checks whether an object is compatible with the
given type. An object of a derived class is also an instance of all its base
classes.
while (t != null)
{
Console.WriteLine(t.Name);
t = t.BaseType;
}
With the BaseType property, we walk the hierarchy up to the
Object type, which has no base type.
$ dotnet run True True True Puppy Dog Animal Object
C# sealed class
The sealed modifier prevents other classes from inheriting from a
class.
var p = new Point(3, 4);
Console.WriteLine(p.Distance());
// class Point3D : Point { } // error CS0509
sealed class Point
{
public int X { get; }
public int Y { get; }
public Point(int x, int y)
{
X = x;
Y = y;
}
public double Distance() => Math.Sqrt(X * X + Y * Y);
}
The Point class is sealed. If we uncomment the
Point3D declaration, the compiler reports the CS0509 error:
a class cannot derive from a sealed type.
$ dotnet run 5
Many classes in .NET, such as string, are sealed. Sealing a class
that is not designed for inheritance is a good practice.
C# hiding members with new
If a derived class declares a member with the same name as a non-virtual member
of its base class, the base member is hidden. The new modifier
states that the hiding is intentional.
Derived d = new Derived();
Base b = d;
d.Info();
b.Info();
class Base
{
public void Info() => Console.WriteLine("Base Info");
}
class Derived : Base
{
public new void Info() => Console.WriteLine("Derived Info");
}
The same object is accessed through variables of two different types.
$ dotnet run Derived Info Base Info
Unlike overriding, hiding is not polymorphic: the called method is chosen by the type of the variable, not by the type of the object. In most cases, overriding virtual methods is the better choice.
C# inheritance vs composition
Inheritance creates a tight coupling between the base and the derived classes. If the relationship between two classes is has-a rather than is-a, we should use composition; that is, a class should contain an instance of the other class.
var car = new Car(new Engine(150));
car.Start();
class Engine
{
public int Power { get; }
public Engine(int power)
{
Power = power;
}
public void Run() => Console.WriteLine($"Engine with {Power} kW is running");
}
class Car
{
private readonly Engine engine;
public Car(Engine engine)
{
this.engine = engine;
}
public void Start()
{
Console.WriteLine("Starting the car");
engine.Run();
}
}
A car is not an engine; it has an engine. Therefore, the Car class
holds a reference to an Engine object instead of deriving from the
Engine class.
$ dotnet run Starting the car Engine with 150 kW is running
Source
In this article we have worked with inheritance in C#.
Author
List all C# tutorials.