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

virtual and override

Let's go back to the fleet. The dispatcher has one instruction for every vehicle: on command, say who you are. The base version of the instruction answers with "a vehicle". But each kind of vehicle may rewrite it for itself: a car answers with "a car", a truck with "a truck".

The dispatcher does not check which vehicle is on the line. One command goes to everyone, and each vehicle follows its own version of the instruction.

In C#, the dispatcher's role is played by code that works with a variable of the base type. A variable of type Vehicle can hold a Car object: every car is a vehicle. Which version of the method does a call through such a variable run?

C#
Vehicle v = new Car();
Console.WriteLine(v.Who()); // car

class Vehicle
{
    public virtual string Who() => "vehicle";
}

class Car : Vehicle
{
    public override string Who() => "car";
}

Let's walk through it line by line:

  1. In Vehicle v = new Car();, the variable has type Vehicle, while the object it leads to has type Car.
  2. virtual in Vehicle lets derived classes rewrite the method Who.
  3. override in Car rewrites it.
  4. v.Who() prints car: the call ran the object's version, even though the variable has type Vehicle.

The variable's type and the object's type

The call v.Who() involves two types, and each decides something different:

  • the variable's type, Vehicle, decides which methods you may call: the compiler checks that Who exists in Vehicle;
  • the object's type, Car, decides which version runs: that is settled at run time, when the object already exists.

A method marked virtual is called a virtual method, and its new version marked override in a derived class is an override. The ability of one and the same call to run different code for objects of different types is called polymorphism. Interviewers often ask about it: "What is polymorphism, and how does it work in C#?"

In the widget, one and the same variable v of type Vehicle leads to three objects in turn. → Car { } reads the same way as in the theme on classes: v leads to a Car object, and the empty braces mean it has no fields. Press "Next step" and watch the top frame: its header names whose version of Who is running.

LiveThe call follows the objectstep 1 of 9 · frames: 1

The variable v of type Vehicle leads to a Vehicle object.

The top frame's header shows whose version of Who runs

The variable's type never changed during the program, yet different versions of Who ran. When a derived class does not override a virtual method, as with Bus, its objects run the version inherited from the base class.

Here the dispatcher analogy is off. In the fleet, any kind of vehicle may rewrite any instruction, while in C# you can override only a virtual method, one marked virtual in the base class.

Calling the base version: base

An override does not have to throw the base version away. Inside a method marked override, you can call it through base.:

C#
Vehicle t = new Taxi();
Console.WriteLine(t.Who()); // taxi, a car

class Taxi : Car
{
    public override string Who() => "taxi, a " + base.Who();
}

This continues the lesson's first program: add the statements after its statements, and the class Taxi at the end of the file. Let's see what happens:

  1. Taxi inherits from Car, and Car from Vehicle. A method marked override stays virtual itself, so Taxi can override the version of Car.
  2. t.Who() runs the object's version, which is Taxi.Who.
  3. base refers to the base class of Taxi, which is Car. So base.Who() runs the version of Car and returns "car".
  4. Taxi.Who puts its own beginning in front of it, and taxi, a car is printed.

Overriding ToString

Every class inherits from object, and object has a virtual method ToString. Console.WriteLine calls it when it prints an object. The version from object returns the name of the object's type. Override ToString, and Console.WriteLine prints whatever you set:

C#
Console.WriteLine(v);    // Car
var bike = new Bike("BMX");
Console.WriteLine(bike); // Bike BMX

class Bike : Vehicle
{
    public string Model;
    public Bike(string model) { Model = model; }
    public override string ToString() => $"Bike {Model}";
}

This fragment continues the first program too:

  1. Console.WriteLine(v) prints Car. Car has no ToString of its own, so the version from object runs. Note that it printed the name of the object's type, not the variable's.
  2. Bike overrides ToString, so Console.WriteLine(bike) prints Bike BMX.

Common mistake

A method was overridden though the base class did not mark it virtual:

C#
Vehicle v = new Car();
Console.WriteLine(v.Who());

class Vehicle
{
    public string Who() => "vehicle";
}

class Car : Vehicle
{
    public override string Who() => "car"; // ✗
}
TEXT
error CS0506: 'Car.Who()': cannot override inherited member 'Vehicle.Who()' because it is not marked virtual, abstract, or override

The program does not compile. In C#, a method is not virtual by default, and Vehicle did not mark Who with the word virtual, so it did not allow rewriting it. Add virtual to the declaration of Who in the class Vehicle, as in the first example.

Key points

  • virtual allows a derived class to rewrite a method, and override rewrites it.
  • The variable's type decides which methods you may call, and the object's type decides which version of a virtual method runs.
  • base.Who() inside an override calls the base class's version.
  • Console.WriteLine(obj) calls ToString; without an override it returns the name of the object's type.