Это работает так.Представьте, что компилятор переписал ваши классы так:
class VTable
{
public VTable(Func<Animal, string> eat)
{
this.AnimalEat = eat;
}
public readonly Func<Animal, string> AnimalEat;
}
class Animal
{
private static AnimalVTable = new VTable(Animal.AnimalEat);
private static string AnimalEat(Animal _this)
{
return "undefined";
}
public VTable VTable;
public static Animal CreateAnimal()
{
return new Animal()
{ VTable = AnimalVTable };
}
}
class Human : Animal
{
private static HumanVTable = new VTable(Human.HumanEat);
private static string HumanEat(Animal _this)
{
return "human";
}
public static Human CreateHuman()
{
return new Human()
{ VTable = HumanVTable };
}
}
class Dog : Animal
{
public static string DogEat(Dog _this) { return "dog"; }
public static Dog CreateDog()
{
return new Dog()
{ VTable = AnimalVTable } ;
}
}
Теперь рассмотрим эти вызовы:
Animal animal;
Dog dog;
animal = new Human();
animal.Eat();
animal = new Animal();
animal.Eat();
dog = new Dog();
dog.Eat();
animal = dog;
animal.Eat();
Компилятор рассуждает следующим образом: Если тип получателя - Animal, тогда вызовКушать должно быть на animal.VTable.AnimalEat.Если тип получателя - Dog, то вызов должен быть DogEat.Таким образом, компилятор записывает их так:
Animal animal;
Dog dog;
animal = Human.CreateHuman(); // sets the VTable field to HumanVTable
animal.VTable.AnimalEat(animal); // calls HumanVTable.AnimalEat
animal = Animal.CreateAnimal(); // sets the VTable field to AnimalVTable
animal.VTable.AnimalEat(animal); // calls AnimalVTable.AnimalEat
dog = Dog.CreateDog(); // sets the VTable field to AnimalVTable
Dog.DogEat(dog); // calls DogEat, obviously
animal = dog;
animal.VTable.AnimalEat(animal); // calls AnimalVTable.AnimalEat
То есть в точности как это работает.Компилятор генерирует vtables для вас за кулисами, и решает во время компиляции, вызывать ли через vtable или нет, основываясь на правилах разрешения перегрузки .
vtables устанавливаются распределителем памяти при создании объекта.(Мой набросок - ложь в этом отношении, поскольку vtable настроен до , вызывается ctor, а не после.)
«this» виртуального метода фактически передается тайнокак невидимый формальный параметр метода.
Имеет смысл?