C#
Virtual dispatch
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
-
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
-
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.
-
Lock the override
public ____ override void Speak() { }
Show answer
public sealed override void Speak() { }
Next lesson: Summary - Polymorphism