CodeBlanks Logo Icon CodeBlanks

Virtual dispatch

C# course · Intermediate · Lesson 63/116

Learn the virtual method table and runtime dispatch.

The virtual method table (vtable)

When a class contains virtual methods, the compiler builds a virtual method table (vtable) behind the scenes. Each object carries a hidden reference to this table. For a virtual call the compiler emits an indirect call: it looks up the real method in the table.

That is why this is called runtime dispatch. A virtual call is slightly slower than a direct call, but on modern hardware the cost is usually negligible. When performance matters you can write sealed override to lock a final override and sometimes shorten the table lookup.

Locking with sealed override

To stop further overriding of an override method, prefix it with sealed: public sealed override void Speak(). This tells the compiler that subclasses of this class can no longer replace the method.

Sealed override provides both safety (prevents accidental overriding) and sometimes performance (may skip the vtable lookup). Do not overuse it: sealing classes reduces extensibility. It is sensible at the leaf nodes of an abstract hierarchy.

Exercise Animal sounds with virtual dispatch

Define Animal with public virtual void Speak() that prints Console.WriteLine("Sound"). Dog : Animal with override prints Console.WriteLine("Woof"). Cat : Animal with override prints Console.WriteLine("Meow"). In Main build a List<Animal> with Dog, Cat, Animal and call Speak in a foreach.

Show example solution
Animal { public override void Speak() { Console.WriteLine("Woof"); } } and class Cat : Animal { public override void Speak() { Console.WriteLine("Meow"); } }. In Main build List<Animal> { new Dog(), new Cat(), new Animal() } and call Speak in a foreach.
Example solution:
using System;
using System.Collections.Generic;

class Animal
{
    public virtual void Speak()
    {
        Console.WriteLine("Sound");
    }
}

class Dog : Animal
{
    public override void Speak()
    {
        Console.WriteLine("Woof");
    }
}

class Cat : Animal
{
    public override void Speak()
    {
        Console.WriteLine("Meow");
    }
}

class Program
{
    static void Main()
    {
        var animals = new List<Animal> { new Dog(), new Cat(), new Animal() };
        foreach (var a in animals)
        {
            a.Speak();
        }
    }
}

Test yourself

  1. Match each keyword to its effect

    virtual
    Creates a vtable slot
    override
    Replaces a vtable slot
    new
    Creates a new slot
    sealed
    Blocks further changes
  2. When does virtual dispatch happen?

    Animal a = new Dog();
    a.Speak();
    • At runtime, based on the object type
    • At compile time, based on the reference type
    • Only when sealed is used
    • Only when new is used
    Show answer

    At runtime, based on the object type — Correct. Virtual dispatch happens at runtime based on the object's type.

  3. Lock the override

    public ____ override void Speak() { }
    Show answer
    public sealed override void Speak() { }

All lessons in Intermediate