Ошибается ли Рихтер, описывая внутреннее устройство вызова невиртуального метода?

Я бы написал этот вопрос напрямую Джеффри Рихтеру, но в прошлый раз он не ответил мне :) так что я постараюсь получить ответ с вашей помощью здесь, ребята :)

В книге "CLR via C #", 3-е издание, на стр.108, Джеффри пишет:

void M3() {
  Employee e;
  e = new Manager();
  year = e.GetYearsEmployed();
  ...
}

следующая строка кода в вызовах M3 Невиртуальный экземпляр сотрудника GetYearsEmployed. При звонке невиртуальный метод экземпляра, JIT компилятор находит объект типа, который соответствует типу переменная, используемая для вызова. В этом случае переменная e равна определяется как Сотрудник. ( Если Тип сотрудника не определяет метод при вызове JIT-компилятор проходит вниз по иерархии классов к объекту ищу этот метод. Это может сделать это потому, что каждый объект типа имеет поле в нем, которое относится к его базе тип; эта информация не отображается в цифры.) Затем JIT-компилятор находит запись в типе объекта таблица методов, которая относится к методу вызывается, JIT выполняет метод (если необходимо), а затем вызывает JITted code.

Когда я прочитал это в первый раз, я подумал, что было бы неэффективно ходить по иерархии классов в поисках метода во время JIT-тинга. Метод легко найти уже на этапе компиляции. Но я верил Джеффри. Я разместил эту информацию на другом форуме, и другой парень подтвердил мои сомнения, что это странно и будет неэффективным, и что, похоже, это неверная информация.

И действительно, если вы посмотрите соответствующий код IL в декомпиляторе, таком как ILDasm или Reflector (я проверил оба) вы увидите, что в IL есть инструкция callvirt, вызывающая метод из базового класса, поэтому JIT не нужно смотреть, в каком классе находится метод во время выполнения:

public class EmployeeBase
{
    public int GetYearsEmployed() { return 1; }
}

public class Employee : EmployeeBase
{
    public void SomeOtherMethod() { }
}

public class Manager : Employee
{
    public void GenProgressReport() { }
}

...

Employee e;
e = new Manager();
int years = e.GetYearsEmployed();

Результат ИЛ это:

L_0000: nop 
L_0001: newobj instance void TestProj.Form1/Manager::.ctor()
L_0006: stloc.0 
L_0007: ldloc.0 
L_0008: callvirt instance int32 TestProj.Form1/EmployeeBase::GetYearsEmployed()

Понимаете? Компилятор уже обнаружил, что метод находится не в классе Employee, но в классе EmployeeBase и отправил правильный вызов. Но, по словам Рихтера, JIT должна была бы выяснить, что метод на самом деле находится в классе EmployeeBase во время выполнения.

Ошибся ли Джеффри Рихтер? Или я чего-то не понимаю?

12
задан Luke Bennett 5 February 2011 в 21:35
поделиться