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[15]: Базовый вопрос по наследованию
От: Sinclair Россия https://github.com/evilguest/
Дата: 07.02.11 11:18
Оценка: 11 (2)
Здравствуйте, vf, Вы писали:

vf>Похоже что, все таки отработает как положено, лениво проверять

Нет. Читать здесь.
Или здесь.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
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[3]: Базовый вопрос по наследованию
От: HowardLovekraft  
Дата: 04.02.11 11:44
Оценка: 2 (1)
Здравствуйте, zfima, Вы писали:

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

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

Явное приведение — это всего лишь команда вида "проверь, является ссылается ли ссылка на объект заданного типа, и, если нет, выбрось исключение". С ее помощью из объекта типа Base объект типа Derived не сделать, если вопрос об этом.
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[8]: Базовый вопрос по наследованию
От: Lloyd Россия  
Дата: 05.02.11 21:05
Оценка: +1
Здравствуйте, MozgC, Вы писали:

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


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

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


У JIT-а нет никакого объекта, он не знает с каким именно объектом будет вызван метод. На вход он получает только IL.
Re[10]: Базовый вопрос по наследованию
От: Lloyd Россия  
Дата: 05.02.11 21:34
Оценка: +1
Здравствуйте, MozgC, Вы писали:

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

L>>Кто такой "type object"?

MC>См. выше в теме, я уже писал.


L>>И зачем он нужен?


MC>Чтобы jit мог обратиться к соответствуешей таблице методов, повторю еще раз цитату:


Тогда я не понимаю, почему тебя вообще мог заинтересовать вопрос "как". Очевидно, рантайм знает, как по типу, указаному в IL-е найти соответствующий type object.

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. 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.


Какая-то мутная формулировка. Тезис про "type didn’t define the method" вообще не воспроизвадится. Код
void Test(Derived obj)
{
    obj.Test();
}

все равно на уровне IL-а транслируется в
callvirt   instance void ConsoleApplication5.Base::Test()

Т.е. этот "walks down" по факту делается не JIT-ом, а компилятором C#/

L>>JIT-у достаточно знать метод, который надо вызвать, а этот метод в явном виде присутствует в IL-е.


MC>При вызове невиртуальных методов jit явно прописывает адрес метода в сгенерированном коде. А этот адрес нужно получить через таблицу методов. А таблица методов получается через type object.
Re[11]: Базовый вопрос по наследованию
От: MozgC США http://nightcoder.livejournal.com
Дата: 05.02.11 22:30
Оценка: +1
Здравствуйте, Jolly Roger, Вы писали:

JR>С тем-же успехом необходимую правку мог сделать ILAsm


А что если бы эта иерархия находилась в другой сборке, и потом ее заменили на новую (без перекомпиляции клиента), в которой метод перенесен в суперкласс? Т.е. возможно джит выполняет функцию дополнительной проверки и поиска на такие случаи?
Re[13]: Базовый вопрос по наследованию
От: MozgC США http://nightcoder.livejournal.com
Дата: 06.02.11 11:24
Оценка: +1
Здравствуйте, Jolly Roger, Вы писали:

JR>С другой стороны, тут есть ещё один момент, который я прошлой ночью упустил — рефлексия. (Ну очень спать хотелось ) Для неё необходимо, чтобы информация о всех членах класса, в том числе невиртуальных методах, была доступна в рантайм. В принципе, JIT может ей воспользоваться при необходимости. Но с другой стороны, ей-же может воспользоваться и C# компилятор, то есть такие вопросы можно вроде как разрешить на этапе компиляции в IL. Иначе у меня не получается непротиворечиво объяснить тот листинг, что я приводил здесь
Автор: Jolly Roger
Дата: 05.02.11
. Ведь в методе Test нет никаких упоминаний BaseObj, однако в IL-листинге он присутствует. Это, по-моему, может означать только одно — C#-компилятор произвёл поиск метода вверх по иерархии и подставил в результирующий IL код его адрес, этот адрес мы и видим в виде метки BaseObj::Sum. Иначе у меня как-то не срастается


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

JR>А вот тот пример с дизассемблированием, правкой и повторной компиляцией с помощью ILasm — а что покажет ILDasm, если ему скормить результат работы ILAsm? Там сохранится внесённое вручную Employee::GetYearsEmployed, или опять оказывается исходная EmployeeBase::GetYearsEmployed?


Ну, я же это написал в том свое посте:

Компилируем с помощью ILAsm.

Проверяем в ILDasm или Reflector'е:

    L_0000: nop 
    L_0001: newobj instance void JitTest.Program/Manager::.ctor()
    L_0006: stloc.0 
    L_0007: ldloc.0 
    L_0008: callvirt instance int32 JitTest.Program/Employee::GetYearsEmployed()


Т.е. проверяем именно скомпилированную ILAsm'ом сборку.
Базовый вопрос по наследованию
От: 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[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[5]: Vtable вручную
От: Qbit86 Кипр
Дата: 04.02.11 12:08
Оценка:
Здравствуйте, zfima, Вы писали:

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


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


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

Когда всё это проделаешь, станет очевидным, что слово virtual является по большему счёту лишь синтаксическим сахаром и дополнительной проверкой типов.
Глаза у меня добрые, но рубашка — смирительная!
Re: Базовый вопрос по наследованию
От: Аноним  
Дата: 04.02.11 12:14
Оценка:
Объект всегда знает какого он типа (obj.GetType())
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[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[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-м. Когда конкретно застал такое поведение — точно не помню, но сейчас уже не канает.
Re[9]: Базовый вопрос по наследованию
От: MozgC США http://nightcoder.livejournal.com
Дата: 05.02.11 21:20
Оценка:
Здравствуйте, Lloyd, Вы писали:

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

L>Кто такой "type object"?

См. выше в теме, я уже писал.

L>И зачем он нужен?


Чтобы jit мог обратиться к соответствуешей таблице методов, повторю еще раз цитату:

(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.


L>JIT-у достаточно знать метод, который надо вызвать, а этот метод в явном виде присутствует в IL-е.


При вызове невиртуальных методов jit явно прописывает адрес метода в сгенерированном коде. А этот адрес нужно получить через таблицу методов. А таблица методов получается через type object.

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

L>У JIT-а нет никакого объекта, он не знает с каким именно объектом будет вызван метод. На вход он получает только IL.

Ага, тут я затупил, ерунду написал
Re[11]: Базовый вопрос по наследованию
От: MozgC США http://nightcoder.livejournal.com
Дата: 05.02.11 21:47
Оценка:
Здравствуйте, Lloyd, Вы писали:

L>Тогда я не понимаю, почему тебя вообще мог заинтересовать вопрос "как". Очевидно, рантайм знает, как по типу, указаному в IL-е найти соответствующий type object.


Видимо да.

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. 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.


L>Какая-то мутная формулировка. Тезис про "type didn’t define the method" вообще не воспроизвадится.

L>Т.е. этот "walks down" по факту делается не JIT-ом, а компилятором C#/

Не факт. Возможно jit не доверяет полностью компилятору, а проверяет, что метод точно существует у указанного типа.
Re[8]: Базовый вопрос по наследованию
От: Jolly Roger  
Дата: 05.02.11 21:55
Оценка:
Здравствуйте, MozgC, Вы писали:

MC>У меня такой вопрос: а как JIT узнает адрес объекта "type object" для базового типа, в котором реально определен метод? В рантайме же у JIT'а будет иметься только объект дочернего типа, и ссылка на его type object. Поэтому JIT'у придется идти по иерархии, чтобы найти type object для базового типа, чтобы уже обратиться к method table базового типа и вызвать этот метод. Тогда может Рихтер прав?

MC>Или JIT может более коротким путем выйти на type object для базового типа в котором реализован метод?

Я думаю, Рихтер просто постарался упростить описание. В рантайме JIT требуется адрес места, где находится IL нужных методов, а этот адрес отлично известен на этапе компиляции. И он непосредственно задаётся компилятором в IL, как и показывает приведённый мною листинг. То есть IL сразу получает указание — "здесь вызывать такой-то метод такого-то базового класса". Где лежит IL этого метода тоже уже известно С#-компилятору, зачем ещё что-то искать? Вообще, для невиртуальных методов нет нужды в каких-либо таблицах, их код может располагаться в любом месте исполняемого образа, хранить указатель на них в рантайме нет необходимости, так как этот адрес подставляется в место вызова на этапе компиляции. Вряд-ли JIT что-то меняет здесь кардинально, ибо бессмысленно. Таблицы методов имеют смысл для виртуальных и интерфейсных методов, так как позволяют создать необходимый для них дополнительный уровень косвенности. Для невиртуальных-же методов этот уровень не нужен, и даже вреден.

Иначе говоря, как-то в рантайм физически связывать тот самый "type object" (по сути — метаданные класса) с невиртуальными методами этого класса нет нужды, а значит и нет смысла перекладывать поиск точек входа таких методов на рантайм, на JIT в том числе.

Впрочем, может быть были какие-то причины сводить в dotnet невиртуальные методы в таблицы, но я их пока не вижу
"Нормальные герои всегда идут в обход!"
Re[10]: Базовый вопрос по наследованию
От: Jolly Roger  
Дата: 05.02.11 22:04
Оценка:
Здравствуйте, samius, Вы писали:

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


Эти времена никуда не ушли, в неуправляемых языках и сейчас так-же Вызов невиртуального метода — это простой call на некоторый код, лежащий в некотором точно известном месте исполняемого образа, с передачей одного "скрытого" параметра — this.
"Нормальные герои всегда идут в обход!"
Re[11]: Базовый вопрос по наследованию
От: samius Япония http://sams-tricks.blogspot.com
Дата: 05.02.11 22:06
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

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


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


JR>Эти времена никуда не ушли, в неуправляемых языках и сейчас так-же Вызов невиртуального метода — это простой call на некоторый код, лежащий в некотором точно известном месте исполняемого образа, с передачей одного "скрытого" параметра — this.

Вообще я говорил конкретно о дотнете.
И кстати, за все неуправляемые говорить не буду, но в C++ для такой ситуации будет UB.
Re[9]: Базовый вопрос по наследованию
От: MozgC США http://nightcoder.livejournal.com
Дата: 05.02.11 22:08
Оценка:
В общем, походу, зря подняли бучу, и Рихтер прав.

Делаем такой проектик:

using System;

namespace JitTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Employee e;
            e = new Manager();
            int years = e.GetYearsEmployed();
            Console.ReadKey();
        }

        public class EmployeeBase
        {
            public int GetYearsEmployed()
            {
                Console.WriteLine("Method in EmployeeBase called.");
                return 1;
            }
        }

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

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

Компилируем, проверяем в ILDasm или Reflector'е:

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

Берем ILDasm и делаем dump в IL.
В открываем исходник в IL и меняем

callvirt instance int32 JitTest.Program/EmployeeBase::GetYearsEmployed()
на
callvirt instance int32 JitTest.Program/Employee::GetYearsEmployed()

Компилируем с помощью ILAsm.

Проверяем в ILDasm или Reflector'е:

    L_0000: nop 
    L_0001: newobj instance void JitTest.Program/Manager::.ctor()
    L_0006: stloc.0 
    L_0007: ldloc.0 
    L_0008: callvirt instance int32 JitTest.Program/Employee::GetYearsEmployed()

Запускаем, видим что нормально вызывается метод класса EmployeeBase.

Т.е. JIT все-таки в рантайме прошелся по иерархии и нашел класс в котором определен метод и сгенерировал правильный вызов.
Re[9]: Базовый вопрос по наследованию
От: samius Япония http://sams-tricks.blogspot.com
Дата: 05.02.11 22:09
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

JR>Впрочем, может быть были какие-то причины сводить в dotnet невиртуальные методы в таблицы, но я их пока не вижу


Таблица нужна хотя бы для того, что бы не JIT-ить вызываемые методы при каждом обращении из JIT-компилируемого в текущий момент метода.
Re[12]: Базовый вопрос по наследованию
От: Jolly Roger  
Дата: 05.02.11 22:11
Оценка:
Здравствуйте, samius, Вы писали:

S>И кстати, за все неуправляемые говорить не буду, но в C++ для такой ситуации будет UB.


Оно будет, если попытаться использовать this, например, попытаться к полям класса. Если не обращаться, то нормально пройдёт.
"Нормальные герои всегда идут в обход!"
Re[13]: Базовый вопрос по наследованию
От: samius Япония http://sams-tricks.blogspot.com
Дата: 05.02.11 22:12
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

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


S>>И кстати, за все неуправляемые говорить не буду, но в C++ для такой ситуации будет UB.


JR>Оно будет, если попытаться использовать this, например, попытаться к полям класса. Если не обращаться, то нормально пройдёт.

Так написано в стандарте?
Re[10]: Базовый вопрос по наследованию
От: Jolly Roger  
Дата: 05.02.11 22:17
Оценка:
Здравствуйте, MozgC, Вы писали:

MC>В общем, походу, зря подняли бучу, и Рихтер прав.


MC>Т.е. JIT все-таки в рантайме прошелся по иерархии и нашел класс в котором определен метод и сгенерировал правильный вызов.


С тем-же успехом необходимую правку мог сделать ILAsm
"Нормальные герои всегда идут в обход!"
Re[14]: Базовый вопрос по наследованию
От: Jolly Roger  
Дата: 05.02.11 22:18
Оценка:
Здравствуйте, samius, Вы писали:

S>Так написано в стандарте?


Нет, а что? Мы разве стандарт обсуждаем? Я как-то не заметил
"Нормальные герои всегда идут в обход!"
Re[15]: Базовый вопрос по наследованию
От: samius Япония http://sams-tricks.blogspot.com
Дата: 05.02.11 22:26
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

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


S>>Так написано в стандарте?


JR>Нет, а что? Мы разве стандарт обсуждаем? Я как-то не заметил


Когда я написал что вызов метода экземпляра у null-а приведет к UB, я имел в виду именно то, что поведение в таком случае неопределено стандартом (или я ошибаюсь?).
Обсуждать сам стандарт C++ я не собирался, т.к. не открывал его.
Re[11]: Базовый вопрос по наследованию
От: vf  
Дата: 05.02.11 22:28
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

Вот здесь есть косвеная информация...

MissingMethodException is thrown if a non-static method with the indicated name and signature could not be found in the class associated with obj or any of its base classes. This is typically detected when Microsoft Intermediate Language (MSIL) instructions are converted to native code, rather than at runtime.

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

MC>А что если бы эта иерархия находилась в другой сборке, и потом ее заменили на новую (без перекомпиляции клиента), в которой метод перенесен в суперкласс? Т.е. возможно джит выполняет функцию дополнительной проверки и поиска на такие случаи?


Может быть. Я, признаться, не вникал в тонкости механики взаимодействия сборок. Можно предположить, что в сборке создаётся нечто вроде таблицы экспорта, через которую такое взаимодействие и происходит. Тогда внутренние изменения не скажутся на внешнем коде, и подобные вопросы можно разрешить на этапе загрузки исполняемого образа, при подгрузке необходимых либ в АП процесса.

С другой стороны, тут есть ещё один момент, который я прошлой ночью упустил — рефлексия. (Ну очень спать хотелось ) Для неё необходимо, чтобы информация о всех членах класса, в том числе невиртуальных методах, была доступна в рантайм. В принципе, JIT может ей воспользоваться при необходимости. Но с другой стороны, ей-же может воспользоваться и C# компилятор, то есть такие вопросы можно вроде как разрешить на этапе компиляции в IL. Иначе у меня не получается непротиворечиво объяснить тот листинг, что я приводил здесь
Автор: Jolly Roger
Дата: 05.02.11
. Ведь в методе Test нет никаких упоминаний BaseObj, однако в IL-листинге он присутствует. Это, по-моему, может означать только одно — C#-компилятор произвёл поиск метода вверх по иерархии и подставил в результирующий IL код его адрес, этот адрес мы и видим в виде метки BaseObj::Sum. Иначе у меня как-то не срастается

А вот тот пример с дизассемблированием, правкой и повторной компиляцией с помощью ILasm — а что покажет ILDasm, если ему скормить результат работы ILAsm? Там сохранится внесённое вручную Employee::GetYearsEmployed, или опять оказывается исходная EmployeeBase::GetYearsEmployed?
"Нормальные герои всегда идут в обход!"
Re[16]: Базовый вопрос по наследованию
От: Jolly Roger  
Дата: 06.02.11 04:21
Оценка:
Здравствуйте, samius, Вы писали:

S>Когда я написал что вызов метода экземпляра у null-а приведет к UB, я имел в виду именно то, что поведение в таком случае неопределено стандартом (или я ошибаюсь?).

S>Обсуждать сам стандарт C++ я не собирался, т.к. не открывал его.

Нет конечно, не ошибаетесь, просто я, видимо, неверно Вас понял Просто смотрел "под углом" обсуждаемой темы, а понимание таких недокументированных тонкостей как раз помогает понять и поведение компилятора.
"Нормальные герои всегда идут в обход!"
Re[12]: Базовый вопрос по наследованию
От: vf  
Дата: 06.02.11 10:40
Оценка:
Здравствуйте, MozgC, Вы писали:

MC>А что если бы эта иерархия находилась в другой сборке, и потом ее заменили на новую (без перекомпиляции клиента), в которой метод перенесен в суперкласс? Т.е. возможно джит выполняет функцию дополнительной проверки и поиска на такие случаи?


А что будет в противоположном случае, когда в другой сборке метод переопределили в наследнике.
Без перекомпиляции приложения, до этого компилятор сам нашел базовый класс и вызывает Base::Method, а сейчас должен вызвать Derived::Method. Без перекомпиляции вызовет не тот метод?!
Re[13]: Базовый вопрос по наследованию
От: vf  
Дата: 06.02.11 11:01
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

JR>Ведь в методе Test нет никаких упоминаний BaseObj, однако в IL-листинге он присутствует.


Судя повсему тот класс который указан в IL предназначен только для пользователя, для компилятора он не нужен и не несет никакой информации.

That is, the method is chosen based on the runtime type of obj rather than the compile-time class visible in the method pointer.


То есть тип объекта, JIT-компилятору (как параметр) не нужен, он его смотрит по obj.

callvirt method

Calls a specific method associated with obj.

The stack transitional behavior, in sequential order, is:
1. An object reference obj is pushed onto the stack.
2. Method arguments arg1 through argN are pushed onto the stack.
3. Method arguments arg1 through argN and the object reference obj are popped from the stack; the method call is performed with these arguments and control is transferred to the method in obj referred to by the method metadata token. When complete, a return value is generated by the callee method and sent to the caller.
4. The return value is pushed onto the stack.

Re[13]: Базовый вопрос по наследованию
От: MozgC США http://nightcoder.livejournal.com
Дата: 06.02.11 11:18
Оценка:
Здравствуйте, vf, Вы писали:

MC>>А что если бы эта иерархия находилась в другой сборке, и потом ее заменили на новую (без перекомпиляции клиента), в которой метод перенесен в суперкласс? Т.е. возможно джит выполняет функцию дополнительной проверки и поиска на такие случаи?


vf>А что будет в противоположном случае, когда в другой сборке метод переопределили в наследнике.

vf>Без перекомпиляции приложения, до этого компилятор сам нашел базовый класс и вызывает Base::Method, а сейчас должен вызвать Derived::Method. Без перекомпиляции вызовет не тот метод?!

В таком случае без перекомпиляции вызовется метод базового класса. Видимо jit просто берет метод указанный в IL и, если он не найден в указанном классе, то идет вверх и ищет где он. А в данном примере в IL останется вызов базового класса, jit увидит, что метод есть у указанного класса и не проверит, что метод уже переопределен в дочернем.
Re[14]: Базовый вопрос по наследованию
От: MozgC США http://nightcoder.livejournal.com
Дата: 06.02.11 11:29
Оценка:
Здравствуйте, vf, Вы писали:

vf>Судя повсему тот класс который указан в IL предназначен только для пользователя, для компилятора он не нужен и не несет никакой информации.


Для JIT'а судя по всему все-таки нужен
Автор: MozgC
Дата: 06.02.11
.

vf>То есть тип объекта, JIT-компилятору (как параметр) не нужен, он его смотрит по obj.


См. выше.
Re[14]: Базовый вопрос по наследованию
От: vf  
Дата: 06.02.11 11:33
Оценка:
Здравствуйте, MozgC, Вы писали:

MC>В таком случае без перекомпиляции вызовется метод базового класса. Видимо jit просто берет метод указанный в IL и, если он не найден в указанном классе, то идет вверх и ищет где он. А в данном примере в IL останется вызов базового класса, jit увидит, что метод есть у указанного класса и не проверит, что метод уже переопределен в дочернем.


Похоже что, все таки отработает как положено, лениво проверять

Вот здесь
Автор: vf
Дата: 06.02.11
, по мотивам первоисточника, я выразил свою мысль: что тип указаный в IL-коде после дисассемблера носит только информационный характер, и предназначен только для пользователя.
Re[15]: Базовый вопрос по наследованию
От: vf  
Дата: 06.02.11 11:36
Оценка:
Здравствуйте, MozgC, Вы писали:

vf>>Судя повсему тот класс который указан в IL предназначен только для пользователя, для компилятора он не нужен и не несет никакой информации.


MC>Для JIT'а судя по всему все-таки нужен
Автор: MozgC
Дата: 06.02.11
.


А зачем? Вся информация о типе объекта у него есть в рантайме.
Re[15]: Базовый вопрос по наследованию
От: MozgC США http://nightcoder.livejournal.com
Дата: 06.02.11 11:46
Оценка:
Здравствуйте, vf, Вы писали:

MC>>В таком случае без перекомпиляции вызовется метод базового класса. Видимо jit просто берет метод указанный в IL и, если он не найден в указанном классе, то идет вверх и ищет где он. А в данном примере в IL останется вызов базового класса, jit увидит, что метод есть у указанного класса и не проверит, что метод уже переопределен в дочернем.


vf>Похоже что, все таки отработает как положено, лениво проверять


Я написал, как отработает. Это не мои фантазии
Re[16]: Базовый вопрос по наследованию
От: vf  
Дата: 06.02.11 12:00
Оценка:
Здравствуйте, MozgC, Вы писали:

MC>Я написал, как отработает. Это не мои фантазии


Да, действительно.
Re[16]: Базовый вопрос по наследованию
От: vf  
Дата: 06.02.11 12:07
Оценка:
Здравствуйте, MozgC, Вы писали:

Покоя нет мне... как же так, выходит что здесь ошибка в описании?! Или я не правильно понял?

That is, the method is chosen based on the runtime type of obj rather than the compile-time class visible in the method pointer.

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

MC>Я так понимаю, что компилятор и JIT-работают вместе. Компилятор может в такой ситуации определить метод какого класса будет вызван, но у JIT'а есть фукнции дополнительной проверки и разрешения конфликтных ситуаций. Samius вчера высказал мнение, что это сделано потому, что IL в принципе может быть кривым, ведь нет гарантий, что он не был создан вручную или каким-то другим компилятором, поэтому JIT должен делать свои проверки и по-возможности разруливать "косяки".


Да, судя по всему так и есть.

JR>>А вот тот пример с дизассемблированием, правкой и повторной компиляцией с помощью ILasm — а что покажет ILDasm, если ему скормить результат работы ILAsm? Там сохранится внесённое вручную Employee::GetYearsEmployed, или опять оказывается исходная EmployeeBase::GetYearsEmployed?


MC>Ну, я же это написал в том свое посте:


Извиняюсь, вчера уже полусонный был, допустил ненимательность, а сегодня не посмотрел ещё раз.
"Нормальные герои всегда идут в обход!"
Re[17]: Базовый вопрос по наследованию
От: MozgC США http://nightcoder.livejournal.com
Дата: 06.02.11 12:37
Оценка:
Здравствуйте, vf, Вы писали:

vf>Покоя нет мне... как же так, выходит что здесь ошибка в описании?! Или я не правильно понял?

vf>

That is, the method is chosen based on the runtime type of obj rather than the compile-time class visible in the method pointer.


Думаю, что неточное описание. Это справедливо для виртуальных функций, а для невиртуальных — не всегда, как показали наши опыты

Хотя, по поводу того, что если в отдельной сборке переопределить метод у дочернего класса и подменить сборку, после чего все равно вызовется метод базового класса, — тут можно выдать это за фичу. Т.е. клиент, когда писал программу, ожидал что вызовется метод базового класса, он не рассчитывал что может появиться метод у дочернего класса, т.к. метод не виртуальный. А тут ему подменивают сборку и поведение. Но все работает как и ожидал клиент при компиляции. Т.е. можно посмотреть на это с этой стороны
Re[18]: Базовый вопрос по наследованию
От: vf  
Дата: 06.02.11 13:45
Оценка:
Здравствуйте, MozgC, Вы писали:

MC>Хотя, по поводу того, что если в отдельной сборке переопределить метод у дочернего класса и подменить сборку, после чего все равно вызовется метод базового класса, — тут можно выдать это за фичу. Т.е. клиент, когда писал программу, ожидал что вызовется метод базового класса, он не рассчитывал что может появиться метод у дочернего класса, т.к. метод не виртуальный. А тут ему подменивают сборку и поведение. Но все работает как и ожидал клиент при компиляции. Т.е. можно посмотреть на это с этой стороны


Да возможно...хотя и не однозначно. Как в описании callvirt мне кажется все таки логичнее. Интересно что:

1. если совсем убить метод в родителе, то получим MissingMethodException
2. а, если сделать метод в родителе абстрактным — тогда вызывается метод потомка

Это все без перекомпиляции самого приложения конечно.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.