开发者

Virtual Functions C#

I understand what a virtual function is. But what I don't get is how do they work internally?

class Animal
{
    virtual string Eat()
    {
        return @"Eat undefined";
    }
}

class Human : Animal
{
    override string Eat()
    {
         return @"Eat like a Human";
    }
}


class Dog : Animal
{
    new string Eat()
    {
         return @"Eat like a Dog";
    }
}

static void Main()
{
    Animal _animal = new Human();
    Console.WriteLine(_animal.Eat());
    _animal = new Dog();
    Console.WriteLine(_animal.Eat());
}

Output for the above gives:

Eat lik开发者_StackOverflow社区e a Human
Eat undefined

In the above code _animal is of type Animal which references a Human object or Dog object. What does this mean? I understand in the memory _animal contains an address which will point to Human or Dog object. How does it decide which function to invoke. In the first case I override and hence child's implementation is called, but in second case I use new and hence the parent's implementation is called. Can you please explain me what happens under the hood?

Thanks in advance Nick


It works like this. Imagine the compiler rewrote your classes into this:

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 } ;
    }
}

Now consider these calls:

Animal animal;
Dog dog;
animal = new Human();
animal.Eat();
animal = new Animal();
animal.Eat();
dog = new Dog();
dog.Eat();
animal = dog;
animal.Eat();

The compiler reasons as follows: If the type of the receiver is Animal then the call to Eat must be to animal.VTable.AnimalEat. If the type of the receiver is Dog then the call must be to DogEat. So the compiler writes these as:

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

That is exactly how it works. The compiler generates vtables for you behind the scenes, and decides at compile time whether to call through the vtable or not based on the rules of overload resolution.

The vtables are set up by the memory allocator when the object is created. (My sketch is a lie in this regard, since the vtable is set up before the ctor is called, not after.)

The "this" of a virtual method is actually secretly passed as an invisible formal parameter to the method.

Make sense?


I understand in the memory _animal contains an address which will point to Human or Dog object. How does it decide which function to invoke.

Like data, code also has an address.

Therefore the typical approach to this problem is for Human or Dog objects to contain the address of the code of their methods. This is sometimes called using a vtable. In a language like C or C++ this concept is also directly exposed as what's called a function pointer.

Now, you've mentioned C#, which has a pretty high-level type system, in which types of objects are also discernible at runtime.... Therefore the implementation details may differ from the traditional approach in some way. But, as to your question, the function pointer/v-table concept is one way to do it, and it would surprise me if .NET has strayed too much from this.


In C#, derived classes must provide the override modifier for any overridden method inherited from a base class.

Animal _animal = new Human();

It's not just the Human object got constructed. They are two sub-objects. One is Animal sub-object and the other is Human sub-object.

Console.WriteLine(_animal.Eat());

When made the call to _animal.Eat();, the run time checks whether the base class method ( i.e., Eat() )is overridden in the derived class. Since, it is overridden, the corresponding derived class method is called. Hence the output -

Eat like a Human

But, in case of -

_animal = new Dog();
Console.WriteLine(_animal.Eat());

In the Dog, there is no Eat() overridden method in the derived class Dog. So, base class method itself is called. Also this method of checking is done because in the base class, Eat() is mentioned as virtual and calling mechanism is decided at run-time. To sum up, virtual calling mechanism is a run-time mechanism.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜