ZetCode

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.

Program.cs
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.

Program.cs
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.

Program.cs
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.

Program.cs
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.

Program.cs
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.

Program.cs
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.

Program.cs
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.

Program.cs
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

Inheritance - C# fundamentals

In this article we have worked with inheritance in C#.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all C# tutorials.