JIT создает для типа таблицу виртуальных методов, через которую идет вызов с заранее подсчитанным индексом
При наследовании и наличии виртуальных методов получается так: от класса к классу они могут только прибавляться в наличии, не пропадая в никуда. причем в каждом последующем классе гарантированно будут присутствовать все методы всех базовых. С одним отличием — при наследовании метод можно переопределить. Потому при построении таблицы методов для какого-то класса, методы базового класса должны находиться по тем же индексам, что и в таблице базового класса. Если метод не переопределен, то и адрес тела метода в таблице будет совпадать с адресом этого метода в таблице базового класса. Если метод переопределен, то значение будет другим. После всех методов базовых классов будут находиться методы ткущего класса. А сам вызов будет происходить так:
- Загрузить адрес таблицы вирт методов.
- Сместиться до начала списка методов
- взять адрес метода по индексу (например) 1
- Вызвать его.
Причем поскольку при наследовании на этом месте всегда будет находиться метод ToString(), то даже переопределяя его в наследниках, будет вызван именно метод ToString(), только не базового класса, а наследника.