Will CLR check the whole inheritance chain to determine which virtual method to call?

后端 未结 2 926
臣服心动
臣服心动 2020-12-09 16:26

The inheritance chain is as follows:

class A
    {
        public virtual void Foo()
        {
            Console.WriteLine(\"A\'s method\");
        }
             


        
相关标签:
2条回答
  • 2020-12-09 17:19

    Amy's answer is correct. Here's how I like to look at this question.

    A virtual method is a slot that can contain a method.

    When asked to do overload resolution, the compiler determines which slot to use at compile time. But the runtime determines what method is actually in that slot.

    Now with that in mind let's look at your example.

    • A has one slot for Foo.
    • B has one slot for Foo, inherited from A.
    • C has two slots for Foo. One inherited from B, and one new. You said you wanted a new slot named Foo, so you got it.
    • D has two slots for Foo, inherited from C.

    That's the slots. So, what goes in those slots?

    • In an A, A.Foo goes in the slot.
    • In a B, B.Foo goes in the slot.
    • In a C, B.Foo goes in the first slot and C.Foo goes in the second slot. Remember, these slots are completely different. You said you wanted two slots with the same name, so that's what you got. If that's confusing, that's your problem. Don't do that if it hurts when you do it.
    • In a D, B.Foo goes in the first slot and D.Foo goes in the second slot.

    So now what happens with your call?

    The compiler reasons that you are calling Foo on something of compile time type A, so it finds the first (and only) Foo slot on A.

    At runtime, the contents of that slot is B.Foo.

    So that's what's called.

    Make sense now?

    0 讨论(0)
  • 2020-12-09 17:27

    When a virtual method is invoked, the run-time type of the object is checked for an overriding member. The overriding member in the most derived class is called, which might be the original member, if no derived class has overridden the member.

    You're starting from the wrong place. Your variable is of type A and contains an instance of D, so the virtual table used is A's 1. Following the text above, we check for an overriding member. We find one in B. C does not count because it is not overriding, it is shadowing the base method. And since D overrides C, not A or B, it doesn't count either. We're looking for the overriding member in the most derived class.

    So the method found is B.Foo().

    If you change C so it overrides instead of shadows, the found method will then be D, because it is the most derived overriding member.

    If you instead change your object to an instance of B or C, B.Foo() will still be the chosen override. To clarify, this is what I mean:

    A tan = new B();
    tan.Foo();    // which foo is called?  Why, the best Foo of course!  B!
    

    The reason B is called is because the inheritance chain we are searching spans from A (the variable type) to B (the runtime type). C and D are no longer part of that chain, and are not part of the virtual table.

    If we instead change the code to this:

    C tan = new D();
    tan.Foo();  // which foo, which foo?
    

    The inheritance chain we search spans from C to D. D is has an overriding member, so it's Foo is called.

    Suppose you add another class Q that inherits from A, and R that inherits from Q, and so forth. You have two branches of inheritance, right? Which is chosen when searching for most derived type? Follow the path from A (your variable type) to D (the runtime type).

    I hope this makes sense.

    1 Not literally. The virtual table belongs to D because it is the runtime type and contains everything in its chain of inheritance, but its useful and easier to think of A as the starting point. You are searching for derived types, after all.

    0 讨论(0)
提交回复
热议问题