Skip to content
Theory · Inheritance and interfaces · lesson 1 of 10

Inheritance

What you'll learn

What you'll learn:

  • declare a derived class and pass arguments to the base constructor with base(…);
  • explain why a call to a virtual method picks the version by the object's real type, and tell overriding with override from hiding with new;
  • describe a shared contract with an abstract class or an interface;
  • check an object's type with is and as.
A chassis Vehicle on top and three vehicles built on the same chassis: a car Car with a horn Honk(), a truck and a bus

Picture a vehicle fleet. It has cars, trucks and buses. You can say the same things about any vehicle in the fleet: it has a name, and it can move. Each kind also has something of its own: a car honks, a truck carries cargo, a bus carries passengers.

At the same time, every car is a vehicle, and every truck is a vehicle too. Whatever a vehicle in general can do, a car can do as well.

How would you describe this fleet with classes? You could write the classes Car, Truck and Bus separately. Then the field Name and the method Move would have to be repeated in each of them, and a bug in Move fixed three times.

Inheritance solves this: you describe what is shared once, in one class, and other classes get it ready-made and add their own parts. The class others inherit from is called the base class, and the inheriting one is the derived class:

C#
var car = new Car();
car.Name = "Lada";
car.Move(); // Lada moves
car.Honk(); // Lada: beep

class Vehicle
{
    public string Name = "";
    public void Move() => Console.WriteLine($"{Name} moves");
}

class Car : Vehicle
{
    public void Honk() => Console.WriteLine($"{Name}: beep");
}

Let's walk through the example line by line:

  1. class Car : Vehicle declares the class Car as derived from Vehicle. Here the colon reads as "is a kind of": a car is a kind of vehicle.
  2. Car declares neither the field Name nor the method Move, yet the object new Car() has both: they came from Vehicle.
  3. car.Move() calls the base class's method and prints Lada moves.
  4. car.Honk() calls Car's own method. Inside Honk, the field Name is available as if it were declared in Car itself.

What a derived class gets

A derived class gets the fields, properties and methods of its base class. The private members are in the object too, but the derived class's code cannot reach them.

If no base class is given, it is object. So every class in C# inherits from object, even when its declaration has no colon.

Here the fleet analogy is off. In real life a car can have several "parents": it is a vehicle, someone's property and goods in a showroom. A class in C# has only one base class, and class Car : Vehicle, Machine does not compile:

TEXT
error CS1721: Class 'Car' cannot have multiple base classes: 'Vehicle' and 'Machine'

Interviewers often ask, "How many base classes can a class have?" For C#, the answer is one.

The base constructor runs first

Constructors are not inherited: each class has its own. When an object of a derived class is created, the constructors run along the chain from the base to the derived class:

C#
var bus = new Bus(); // Vehicle
                     // Bus

class Vehicle
{
    public Vehicle() => Console.WriteLine("Vehicle");
}

class Bus : Vehicle
{
    public Bus() => Console.WriteLine("Bus");
}

Let's see what happens on new Bus():

  1. The constructor of Bus is called.
  2. Before running its body, C# calls the constructor of the base class Vehicle, which prints Vehicle.
  3. Only then does the body of the Bus constructor run: it prints Bus.

So by the time the derived constructor's body starts, the part of the object that comes from the base class is already set up.

Calling base(…)

The Vehicle constructor in the example above has no parameters, so C# calls it on its own. When the base constructor needs arguments, the derived class's constructor passes them with : base(…) right after its own parameter list:

C#
var car = new Car("Lada");
car.Honk(); // Lada: beep

class Vehicle
{
    protected string Name;
    public Vehicle(string name) { Name = name; }
}

class Car : Vehicle
{
    public Car(string name) : base(name) { }
    public void Honk() => Console.WriteLine($"{Name}: beep");
}

Let's go through it step by step:

  1. new Car("Lada") calls the Car constructor, and its parameter name gets "Lada".
  2. : base(name) passes that value to the Vehicle constructor, which runs first: the field Name gets "Lada".
  3. The body of the Car constructor is empty: the base constructor has already done the work.
  4. car.Honk() prints Lada: beep.

: base(…) looks like : this(…) from the theme on classes. The difference is that : this(…) calls another constructor of the same class, while : base(…) calls the base class's constructor.

The protected modifier

The field Name in this example is declared protected. It is an access modifier that sits between the familiar public and private:

  • public: the member is visible to all code;
  • protected: the member is visible to the class itself and to its derived classes;
  • private: the member is visible only to the class itself.

That is why the method Honk in Car reads Name, while the line Console.WriteLine(car.Name); among the top-level statements does not compile:

TEXT
error CS0122: 'Vehicle.Name' is inaccessible due to its protection level

protected fits what the derived classes need but the rest of the program does not.

Common mistake

The derived class has its own constructor, but it does not call the base constructor:

C#
var car = new Car("Lada");

class Vehicle
{
    public string Name;
    public Vehicle(string name) { Name = name; }
}

class Car : Vehicle
{
    public Car(string name) // ✗
    {
        Name = name;
    }
}
TEXT
error CS7036: There is no argument given that corresponds to the required parameter 'name' of 'Vehicle.Vehicle(string)'

The program does not compile. To a constructor without : base(…), C# adds a call to the base class's constructor with no arguments. But Vehicle has no such constructor: it needs name. The same error appears if Car has no constructor at all: the default constructor also calls the base one with no arguments.

The fix is public Car(string name) : base(name) { }: the Vehicle constructor fills in the field Name.

Key points

  • class Car : Vehicle makes Car derived from Vehicle: it gets the base class's members and adds its own.
  • A class in C# has one base class; if none is given, it is object.
  • When an object is created, the base class constructor runs first, then the derived one; arguments go to the base constructor through : base(…).
  • protected opens a member to the class itself and its derived classes, but not to the rest of the code.