Базовый вопрос по наследованию
От: zfima  
Дата: 04.02.11 11:15
Оценка:
Скажем, есть базовый и наследуемые классы: B и D

class Base
{
//...
}

class Derived: Base
{
//...
}



И, конечно можно ссылке на базовый класс присвоить ссылку на объект производного класса
Derived der1 = new Derived();
Base base1 = der1;
Ну, а потом, можно так:
Derived der2 = base1;

А вот так не "прокатит":
Base base2 = new Base();
Derived der3 = base2;
Т.к base2 не является ссылкой на объект типа Derived

Вопрос: как вообще объект базового класс может "знать", что указывает на более расширенный класс? Он, как бы, должен быть расширенным классом, чтобы знать об этом...
Re: Базовый вопрос по наследованию
От: Qbit86 Кипр
Дата: 04.02.11 11:23
Оценка:
Здравствуйте, zfima, Вы писали:

Z>Ну, а потом, можно так:

Z>Derived der2 = base1;

Ты это проверял?

Z>Derived der3 = base2;


Компилятор эту строчку будет трактовать так же, как и предыдущую, независимо от контекста.
Глаза у меня добрые, но рубашка — смирительная!
Re[2]: Базовый вопрос по наследованию
От: zfima  
Дата: 04.02.11 11:32
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>Здравствуйте, zfima, Вы писали:


Z>>Ну, а потом, можно так:

Z>>Derived der2 = base1;

Q>Ты это проверял?

Извеняюсь, забыл кастинг:
Derived der2 = (Derived)base1;

Z>>Derived der3 = base2;


Q>Компилятор эту строчку будет трактовать так же, как и предыдущую, независимо от контекста.


Трактует то конечно, может и одинакого, но во время исполнения первый вариант работает а второй нет
Re[3]: Базовый вопрос по наследованию
От: Qbit86 Кипр
Дата: 04.02.11 11:41
Оценка: 3 (1)
Здравствуйте, zfima, Вы писали:

Z>Трактует то конечно, может и одинакого, но во время исполнения первый вариант работает а второй нет


Если ты хорошо подумаешь, то поймёшь, что твоя исходная формулировка эквивалентна следующей:

Почему эти две команды Console.WriteLine выводят разные значения?

Base base1 = new Derived();
Console.WriteLine(base1.GetType());
Base base2 = new Base();
Console.WriteLine(base2.GetType());


Чтобы понять, как это работает, необходимо иметь представление о таблице виртуальных методов.
Глаза у меня добрые, но рубашка — смирительная!
Re[4]: Базовый вопрос по наследованию
От: zfima  
Дата: 04.02.11 11:43
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>Здравствуйте, zfima, Вы писали:


Z>>Трактует то конечно, может и одинакого, но во время исполнения первый вариант работает а второй нет


Q>Если ты хорошо подумаешь, то поймёшь, что твоя исходная формулировка эквивалентна следующей:


Q>

Q>Почему эти две команды Console.WriteLine выводят разные значения?
Q>

Q>Base base1 = new Derived();
Q>Console.WriteLine(base1.GetType());
Q>Base base2 = new Base();
Q>Console.WriteLine(base2.GetType());
Q>


Q>Чтобы понять, как это работает, необходимо иметь представление о таблице виртуальных методов.



Спасибо, Qbit86
Будем апгрэйдиться!
Re[3]: Базовый вопрос по наследованию
От: HowardLovekraft  
Дата: 04.02.11 11:44
Оценка: 2 (1)
Здравствуйте, zfima, Вы писали:

Z>Трактует то конечно, может и одинакого, но во время исполнения первый вариант работает а второй нет

Есть объект определенного типа, а есть ссылка на этот объект.
В первом варианте base1 ссылается на объект типа Derived, а во втором base2 ссылается на объект типа Base. Поэтому такой результат.

Явное приведение — это всего лишь команда вида "проверь, является ссылается ли ссылка на объект заданного типа, и, если нет, выбрось исключение". С ее помощью из объекта типа Base объект типа Derived не сделать, если вопрос об этом.
Re[5]: Vtable вручную
От: Qbit86 Кипр
Дата: 04.02.11 12:08
Оценка:
Здравствуйте, zfima, Вы писали:

Q>>Чтобы понять, как это работает, необходимо иметь представление о таблице виртуальных методов.


Z>Будем апгрэйдиться!


Лучший способ проникнуться полиморфизмом-на-основе-позднего-связывания — это реализовать его вручную. Возьми язык Си (или С++/C#, но без использования слова virtual, только указатели на функции или делегаты) и реализуй на нём пример полиморфного поведения. У тебя будет один класс — пачка функций, будет несколько его предопределённых неизменяемых экземпляров (по числу классов в иерархии), проинициализированных нужными функциями. Классы твоей доменной области будут инициализироваться ссылкой на эту таблицу функций; разные экземпляры одного класса будут инициализироваться ссылкой на одну и ту же общую таблицу.

Когда всё это проделаешь, станет очевидным, что слово virtual является по большему счёту лишь синтаксическим сахаром и дополнительной проверкой типов.
Глаза у меня добрые, но рубашка — смирительная!
Re: Базовый вопрос по наследованию
От: Аноним  
Дата: 04.02.11 12:14
Оценка:
Объект всегда знает какого он типа (obj.GetType())
Re: Базовый вопрос по наследованию
От: MozgC США http://nightcoder.livejournal.com
Дата: 04.02.11 18:56
Оценка: 80 (5)
Здравствуйте, zfima, Вы писали:

Z>Вопрос: как вообще объект базового класс может "знать", что указывает на более расширенный класс? Он, как бы, должен быть расширенным классом, чтобы знать об этом...


Во-первых, каждый класс в управляемой куче имеет некоторый заголовок, хранящий, помимо прочих служебных полей, указатель на специальный объект типа RuntimeType, который называется "type object". Этот указатель в заголовке каждого объекта инициализируется при создании объекта. Для каждого класса создается только один "type object". И каждый экземпляр одного и того же класса будет содержать в себе указатель на один и тот же "type object" для этого конкретного класса. Объект "type object" содержит в себе статические поля соответствующего класса, таблицу методов и прочие служебные поля. Нас же интересует таблица методов. Таблица методов содержит слоты для всех методов, реализованных конкретным классом, а так же слоты виртуальных методов, унаследованных от родителей (даже если класс их не переопределяет). Каждый слот таблицы методов содержит указатель на адрес в памяти, где находится некоторый код. Изначально, там находятся инструкции вызывающие компиляцию метода в нативный код и последующее его выполнение, но после компиляции метода на место этих инструкций записывается инструкция перехода (jmp) на код, содержащий тело уже скомпилированного в нативный код метода.
Вернемся чуть-чуть назад. Во время выполнения программы при первом обращении к классу (не объекту, а классу) создается соответствующий "type object" и заполняются слоты таблицы методов.
Теперь рассмотрим 3 основных случая: вызов невиртуального метода, вызов виртуального метода и вызов интерфейсного метода.

1) В случае вызова невиртуального метода происходит обращение к обьекту "type object" соответствующему типу переменной (не реальному типу объекта), т.е. для кода
Base b = new Derived();
b.SomeMethod(); // SomeMethod is not virtual

при jit-компиляции этого кода jit-компилятор обратится к Base Type Object и проверит его таблицу методов на содержание там метода SomeMethod(). Если метод SomeMethod() найден в таблице методов для типа Base, то jit-компилятор возьмет его адрес (сначала скомпилировов, если он еще не был скомпилирован в нативный код) и запишет инструкцию call с этим адресом. Если метод SomeMethod() не найден в таблице методов для типа Base, то jit-компилятор произведет поиск вверх по иерархии по направлению к типу Object пока не найдет этот метод. Таким образом будет подготовлен и произведен вызов невиртуального метода.

2) В случае вызова виртуального метода
Base b = new Derived();
b.SomeVirtualMethod();

jit-компилятор создаст чуть более сложный код. Этот код будет получать указатель на реальный "type object" для нашего объекта, т.е. для данного примера указатель на Derived Type Object, далее будет идти обращение к таблице методов для типа Derived, получение адреса метода SomeVirtualMethod() и вызов метода по этому адресу. Если метод SomeVirtualMethod() не переопределен в типе Derived, то слот SomeVirtualMethod в таблице методов типа Derived будет указывать на то же самое место, что и слот SomeVirtualMethod в таблице методов типа Base. Вот так происходит подготовка и вызов виртуального метода.

3) В случае вызова интерфейсного метода
IBase ib = new Derived();
ib.SomeInterfaceMethod();

jit-компилятор создаст еще чуть более сложный код, хотя механизм похож на вызов виртуального метода. Дело в том, что в таблице методов есть ссылка на еще одну таблицу — таблицу интерфейсных методов (ну это я ее так называю, в разных источниках она по-разному называется, например Interface Offset Table или Domain Interface VTable Map). Каждой реализации интерфейса будет соответствовать один слот в этой таблице интерфейсных методов. Каждый слот в свою очередь будет указывать обратно на таблицу методов, а точнее на первый слот интерфейсного метода данного интерфейса (см. картинку ниже чтобы понять эту кашу). Таким образом, при вызове интерфейсного метода добавляются (по сравнению с вызовом виртуального метода) инструкции получения сначала адреса таблицы интерфейсных методов, а потом адреса слота в таблице методов типа.




Более-менее подробно об этом всем можно прочитать у рихтера в "CLR via C#",
еще подробнее (да, такое тоже бывает ) здесь: Drill Into .NET Framework Internals to See How the CLR Creates Runtime Objects
и здесь: .NET Type Internals — From a Microsoft CLR Perspective
virtual method table method table таблица виртуальных методов вызов виртуального метода вызов интерфейсного метода virtual method call interface method call c# .net
Re[2]: Базовый вопрос по наследованию
От: zfima  
Дата: 04.02.11 20:55
Оценка:
Здравствуйте, MozgC, Вы писали:

MC>Здравствуйте, zfima, Вы писали:


Z>>Вопрос: как вообще объект базового класс может "знать", что указывает на более расширенный класс? Он, как бы, должен быть расширенным классом, чтобы знать об этом...


MC>Во-первых, каждый класс в управляемой куче имеет некоторый заголовок, хранящий, помимо прочих служебных полей, указатель на специальный объект типа RuntimeType, который называется "type object". Этот указатель в заголовке каждого объекта инициализируется при создании объекта. Для каждого класса создается только один "type object". И каждый экземпляр одного и того же класса будет содержать в себе указатель на один и тот же "type object" для этого конкретного класса. Объект "type object" содержит в себе статические поля соответствующего класса, таблицу методов и прочие служебные поля. Нас же интересует таблица методов. Таблица методов содержит слоты для всех методов, реализованных конкретным классом, а так же слоты виртуальных методов, унаследованных от родителей (даже если класс их не переопределяет). Каждый слот таблицы методов содержит указатель на адрес в памяти, где находится некоторый код. Изначально, там находятся инструкции вызывающие компиляцию метода в нативный код и последующее его выполнение, но после компиляции метода на место этих инструкций записывается инструкция перехода (jmp) на код, содержащий тело уже скомпилированного в нативный код метода.

MC>Вернемся чуть-чуть назад. Во время выполнения программы при первом обращении к классу (не объекту, а классу) создается соответствующий "type object" и заполняются слоты таблицы методов.
MC>Теперь рассмотрим 3 основных случая: вызов невиртуального метода, вызов виртуального метода и вызов интерфейсного метода.

MC>1) В случае вызова невиртуального метода происходит обращение к обьекту "type object" соответствующему типу переменной (не реальному типу объекта), т.е. для кода

MC>
Base b = new Derived();
MC>b.SomeMethod(); // SomeMethod is not virtual

MC>при jit-компиляции этого кода jit-компилятор обратится к Base Type Object и проверит его таблицу методов на содержание там метода SomeMethod(). Если метод SomeMethod() найден в таблице методов для типа Base, то jit-компилятор возьмет его адрес (сначала скомпилировов, если он еще не был скомпилирован в нативный код) и запишет инструкцию call с этим адресом. Если метод SomeMethod() не найден в таблице методов для типа Base, то jit-компилятор произведет поиск вверх по иерархии по направлению к типу Object пока не найдет этот метод. Таким образом будет подготовлен и произведен вызов невиртуального метода.

MC>2) В случае вызова виртуального метода

MC>
Base b = new Derived();
MC>b.SomeVirtualMethod();

MC>jit-компилятор создаст чуть более сложный код. Этот код будет получать указатель на реальный "type object" для нашего объекта, т.е. для данного примера указатель на Derived Type Object, далее будет идти обращение к таблице методов для типа Derived, получение адреса метода SomeVirtualMethod() и вызов метода по этому адресу. Если метод SomeVirtualMethod() не переопределен в типе Derived, то слот SomeVirtualMethod в таблице методов типа Derived будет указывать на то же самое место, что и слот SomeVirtualMethod в таблице методов типа Base. Вот так происходит подготовка и вызов виртуального метода.

MC>3) В случае вызова интерфейсного метода

MC>
IBase ib = new Derived();
MC>ib.SomeInterfaceMethod();

MC>jit-компилятор создаст еще чуть более сложный код, хотя механизм похож на вызов виртуального метода. Дело в том, что в таблице методов есть ссылка на еще одну таблицу — таблицу интерфейсных методов (ну это я ее так называю, в разных источниках она по-разному называется, например Interface Offset Table или Domain Interface VTable Map). Каждой реализации интерфейса будет соответствовать один слот в этой таблице интерфейсных методов. Каждый слот в свою очередь будет указывать обратно на таблицу методов, а точнее на первый слот интерфейсного метода данного интерфейса (см. картинку ниже чтобы понять эту кашу). Таким образом, при вызове интерфейсного метода добавляются (по сравнению с вызовом виртуального метода) инструкции получения сначала адреса таблицы интерфейсных методов, а потом адреса слота в таблице методов типа.

MC>



MC>Более-менее подробно об этом всем можно прочитать у рихтера в "CLR via C#",

MC>еще подробнее (да, такое тоже бывает ) здесь: Drill Into .NET Framework Internals to See How the CLR Creates Runtime Objects
MC>и здесь: .NET Type Internals — From a Microsoft CLR Perspective

MozgC, респект обширному богатству знаний!
Спасибо!
Re[2]: Базовый вопрос по наследованию
От: Jolly Roger  
Дата: 05.02.11 06:10
Оценка:
Здравствуйте, MozgC, Вы писали:

MC>1) В случае вызова невиртуального метода происходит обращение к обьекту "type object" соответствующему типу переменной (не реальному типу объекта), т.е. для кода

MC>
Base b = new Derived();
MC>b.SomeMethod(); // SomeMethod is not virtual

MC>при jit-компиляции этого кода jit-компилятор обратится к Base Type Object и проверит его таблицу методов на содержание там метода SomeMethod(). Если метод SomeMethod() найден в таблице методов для типа Base, то jit-компилятор возьмет его адрес (сначала скомпилировов, если он еще не был скомпилирован в нативный код) и запишет инструкцию call с этим адресом. Если метод SomeMethod() не найден в таблице методов для типа Base, то jit-компилятор произведет поиск вверх по иерархии по направлению к типу Object пока не найдет этот метод. Таким образом будет подготовлен и произведен вызов невиртуального метода.

По поводу выделенной фразы — это точно? Признаться, есть некоторые сомнения. Просто не вижу необходимости возлагать это на JIT, так как вся необходимая информация доступна уже на этапе компиляции в IL, по-моему
"Нормальные герои всегда идут в обход!"
Re[3]: Базовый вопрос по наследованию
От: MozgC США http://nightcoder.livejournal.com
Дата: 05.02.11 11:50
Оценка: +1
Здравствуйте, Jolly Roger, Вы писали:

MC>>1) В случае вызова невиртуального метода происходит обращение к обьекту "type object" соответствующему типу переменной (не реальному типу объекта), т.е. для кода

MC>>
Base b = new Derived();
MC>>b.SomeMethod(); // SomeMethod is not virtual

MC>>при jit-компиляции этого кода jit-компилятор обратится к Base Type Object и проверит его таблицу методов на содержание там метода SomeMethod(). Если метод SomeMethod() найден в таблице методов для типа Base, то jit-компилятор возьмет его адрес (сначала скомпилировов, если он еще не был скомпилирован в нативный код) и запишет инструкцию call с этим адресом. Если метод SomeMethod() не найден в таблице методов для типа Base, то jit-компилятор произведет поиск вверх по иерархии по направлению к типу Object пока не найдет этот метод. Таким образом будет подготовлен и произведен вызов невиртуального метода.

JR>По поводу выделенной фразы — это точно? Признаться, есть некоторые сомнения. Просто не вижу необходимости возлагать это на JIT, так как вся необходимая информация доступна уже на этапе компиляции в IL, по-моему


CLR via C#, 3rd edition, p.108:

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

The next line of code in M3 calls Employee’s nonvirtual instance GetYearsEmployed method. When calling a nonvirtual instance method, the JIT compiler locates the type object that corresponds
to the type of the variable being used to make the call. In this case, the variable e is defined as an Employee. (If the Employee type didn’t define the method being called, the JIT compiler walks down the class hierarchy toward Object looking for this method. It can do this because each type object has a field in it that refers to its base type; this information is not shown in the figures.) Then, the JIT compiler locates the entry in the type object’s method table that refers to the method being called, JITs the method (if necessary), and then calls the JITted code.

Re[4]: Базовый вопрос по наследованию
От: Jolly Roger  
Дата: 05.02.11 12:36
Оценка: +1
Здравствуйте, MozgC, Вы писали:

MC>CLR via C#, 3rd edition, p.108:


MC>[q]
void M3() {
MC>  Employee e = new Manager();
MC>  year = e.GetYearsEmployed();
MC>  ...
MC>}

MC>(If the Employee type didn’t define the method being called, the JIT compiler walks down the class hierarchy toward Object looking for this method.

И всё-же это моих сомнений не развеяло Может я неправильно понимаю IL, но вот такой код

    static int y;

    public void Test()
    {
        DerivedObj obj = new DerivedObj()
        var y1 = obj.Mul(2, 4);
        var y2 = obj.Sum(5, 10);
        y = y1 + y2;
    }
}

public class BaseObj
{
    public int Sum(int x1, int x2)
    {
        return x1 + x2;
    }
}

public class DerivedObj : BaseObj
{
    public int Mul(int x1, int x2)
    {
        return x1 * x2;
    }
}

даёт вот такой IL

.method public hidebysig instance void  Test() cil managed
{
  // Code size       34 (0x22)
  .maxstack  3
  .locals init ([0] class CarpetCleaner.DerivedObj obj,
           [1] int32 y1,
           [2] int32 y2)
  IL_0000:  newobj     instance void CarpetCleaner.DerivedObj::.ctor()
  IL_0005:  stloc.0
  IL_0006:  ldloc.0
  IL_0007:  ldc.i4.2
  IL_0008:  ldc.i4.4
  IL_0009:  callvirt   instance int32 CarpetCleaner.DerivedObj::Mul(int32,
                                                                    int32)
  IL_000e:  stloc.1
  IL_000f:  ldloc.0
  IL_0010:  ldc.i4.5
  IL_0011:  ldc.i4.s   10
  IL_0013:  callvirt   instance int32 CarpetCleaner.BaseObj::Sum(int32,
                                                                 int32)
  IL_0018:  stloc.2
  IL_0019:  ldloc.1
  IL_001a:  ldloc.2
  IL_001b:  add
  IL_001c:  stsfld     int32 CarpetCleaner.Form1::y
  IL_0021:  ret
} // end of method Form1::Test

Т.е. сразу указан вызов BaseObj::Sum Да и здравый смысл подсказывает, что возлагать поиск невиртуального метода на JIT как-бы не очень экономно
"Нормальные герои всегда идут в обход!"
Re[5]: Базовый вопрос по наследованию
От: samius Япония http://sams-tricks.blogspot.com
Дата: 05.02.11 20:16
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

JR>Т.е. сразу указан вызов BaseObj::Sum Да и здравый смысл подсказывает, что возлагать поиск невиртуального метода на JIT как-бы не очень экономно


А какие есть предложения по поводу того, кто же будет искать адрес метода CarpetCleaner.BaseObj::Sum более экономно?
Re[6]: Базовый вопрос по наследованию
От: Jolly Roger  
Дата: 05.02.11 20:37
Оценка:
Здравствуйте, samius, Вы писали:

S>А какие есть предложения по поводу того, кто же будет искать адрес метода CarpetCleaner.BaseObj::Sum более экономно?


Ну во-первых, всё-таки сразу имеет место обращение к типу BaseObj, без какого-либо поиска в DerivedObj и прохода по списку наследования, о чём говорилось здесь
Автор: MozgC
Дата: 04.02.11
. А во-вторых, зачем вообще что-то искать? Точка входа интересующего метода известна на этапе компиляции, искать её во время выполнения нет нужды. Можно просто подставить адрес этой точки в место вызова. Компиляторы неуправляемых языков так и поступают, а в случае dotnet разница, в принципе, должна быть только в том, что в этой точке до первого обращения будет находиться jmp на JIT, а после — код самого метода. По-моему так
"Нормальные герои всегда идут в обход!"
Re[7]: Базовый вопрос по наследованию
От: MozgC США http://nightcoder.livejournal.com
Дата: 05.02.11 20:49
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

JR>Ну во-первых, всё-таки сразу имеет место обращение к типу BaseObj, без какого-либо поиска в DerivedObj и прохода по списку наследования, о чём говорилось здесь
Автор: MozgC
Дата: 04.02.11
. А во-вторых, зачем вообще что-то искать? Точка входа интересующего метода известна на этапе компиляции, искать её во время выполнения нет нужды. Можно просто подставить адрес этой точки в место вызова. Компиляторы неуправляемых языков так и поступают, а в случае dotnet разница, в принципе, должна быть только в том, что в этой точке до первого обращения будет находиться jmp на JIT, а после — код самого метода. По-моему так


У меня такой вопрос: а как JIT узнает адрес объекта "type object" для базового типа, в котором реально определен метод? В рантайме же у JIT'а будет иметься только объект дочернего типа, и ссылка на его type object. Поэтому JIT'у придется идти по иерархии, чтобы найти type object для базового типа, чтобы уже обратиться к method table базового типа и вызвать этот метод. Тогда может Рихтер прав?
Или JIT может более коротким путем выйти на type object для базового типа в котором реализован метод?
Re[7]: Базовый вопрос по наследованию
От: samius Япония http://sams-tricks.blogspot.com
Дата: 05.02.11 20:59
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

JR>Здравствуйте, samius, Вы писали:


S>>А какие есть предложения по поводу того, кто же будет искать адрес метода CarpetCleaner.BaseObj::Sum более экономно?


JR>Ну во-первых, всё-таки сразу имеет место обращение к типу BaseObj, без какого-либо поиска в DerivedObj и прохода по списку наследования, о чём говорилось здесь
Автор: MozgC
Дата: 04.02.11
.

Тут вопрос скорее того, нафига нужен callvirt для вызова невиртуальных методов, и рассчитывает ли JIT на то что искомый метод в точности указана в IL...

JR>А во-вторых, зачем вообще что-то искать? Точка входа интересующего метода известна на этапе компиляции, искать её во время выполнения нет нужды.

Во время выполнения не ищется, ищется во время JIT компиляции
JR>Можно просто подставить адрес этой точки в место вызова. Компиляторы неуправляемых языков так и поступают, а в случае dotnet разница, в принципе, должна быть только в том, что в этой точке до первого обращения будет находиться jmp на JIT, а после — код самого метода. По-моему так
Но вообще говоря в IL указан callvirt, потому вполне мог подразумеваться Derived::Sum, если таковой есть
Re[8]: Базовый вопрос по наследованию
От: MozgC США http://nightcoder.livejournal.com
Дата: 05.02.11 21:02
Оценка:
Здравствуйте, samius, Вы писали:

S>Тут вопрос скорее того, нафига нужен callvirt для вызова невиртуальных методов


Why does C# always use callvirt?
Re[8]: Базовый вопрос по наследованию
От: Lloyd Россия  
Дата: 05.02.11 21:05
Оценка: +1
Здравствуйте, MozgC, Вы писали:

MC>У меня такой вопрос: а как JIT узнает адрес объекта "type object" для базового типа, в котором реально определен метод?


Кто такой "type object"? И зачем он нужен? JIT-у достаточно знать метод, который надо вызвать, а этот метод в явном виде присутствует в IL-е.
На всякий случай, не забывайте, что метод у объекта — это простая "процедура", принимающая дополнительный параметр.

MC>В рантайме же у JIT'а будет иметься только объект дочернего типа, и ссылка на его type object.


У JIT-а нет никакого объекта, он не знает с каким именно объектом будет вызван метод. На вход он получает только IL.
Re[9]: Базовый вопрос по наследованию
От: samius Япония http://sams-tricks.blogspot.com
Дата: 05.02.11 21:19
Оценка:
Здравствуйте, MozgC, Вы писали:

MC>Здравствуйте, samius, Вы писали:


S>>Тут вопрос скорее того, нафига нужен callvirt для вызова невиртуальных методов


MC>Why does C# always use callvirt?


Выглядит нелепо, т.к. я еще застал те времена, когда вызов невиртуального метода у null прокатывал. А общение с дотнетом я начал году в 2003-м. Когда конкретно застал такое поведение — точно не помню, но сейчас уже не канает.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.