More about Virtual / new…plus interfaces!

前端 未结 6 910

Yesterday I posted a question about the new/virtual/override keywords, and i learned a lot from your answers. But still i remain with some doubts.

In between all the

6条回答
  •  难免孤独
    2021-01-01 06:00

    Good question.

    The way to think about this is: interfaces get their own set of slots. A class which implements an interface is required to fill in those slots.

    • Interface I1 has a slot we'll call I1SLOT.
    • Interface I1 has a slot we'll call I2SLOT.
    • Class A has two slots of its own, AMinSLOT and ADrawSLOT.
    • Class A has three methods, which we'll call AMinMethod, ADrawMethod and AI2DrawMethod.
    • When you say "new A", the runtime has four slots to fill in.
    • I1SLOT is filled in with ADrawMethod.
    • I2SLOT is filled in with AI2DrawMethod.
    • AMinSLOT is filled in with AMinMethod.
    • ADrawSLOT is filled in with ADrawMethod.
    • Class B has three slots. It inherits AMinSLOT and ADrawSLOT, and defines a new slot, BDrawSLOT.
    • Class B has two methods, BDrawMethod and BI1DrawMethod.
    • When you say "new B" the runtime has five slots to fill in.
    • I1SLOT is filled in with BI1DrawMethod.
    • I2SLOT is filled in with BDrawMethod.
    • AMinSLOT is filled in with AMinMethod.
    • ADrawSLOT is filled in with ADrawMethod.
    • BDrawSLOT is filled in with BDrawMethod.

    Now remember, the job of overload resolution is to choose the slot based on the type and the arguments. There are no arguments, so the compiler only has the type to go off of.

    • When you call Draw on an object of compile-time type A, the best match is ADrawSLOT.
    • When you call Draw on an object of compile-time type B, the best match is BDrawSLOT.
    • When you call Draw on an object of compile-time type I1, the best match is I1SLOT.
    • When you call Draw on an object of compile-time type I2, the best match is I2SLOT.

    And the compiler generates code that says "call whatever method is in the chosen slot at runtime."

    Summing up:

    A a1 = new A();
    A a2 = new B();
    B b = new B();
    (a1 as A).Draw();  // ADrawSLOT contains A::Draw
    (a1 as I1).Draw(); // I1SLOT    contains A::Draw
    (a1 as I2).Draw(); // I2SLOT    contains A::I2.Draw
    (a2 as A).Draw();  // ADrawSLOT contains A::Draw
    (a2 as B).Draw();  // BDrawSLOT contains B::Draw
    (a2 as I1).Draw(); // I1SLOT    contains B::I1.Draw
    (a2 as I2).Draw(); // I2SLOT    contains B::Draw
    (b as A).Draw();   // ADrawSLOT contains A::Draw
    (b as B).Draw();   // BDrawSLOT contains B::Draw
    (b as I1).Draw();  // I1SLOT    contains B::I1Draw
    (b as I2).Draw();  // I2SLOT    contains B::Draw
    

    If you're interested in how this is implemented, use ILDASM to disassemble your program, and then look at metadata table 25 (0x19), the MethodImpl table. This program's MethodImplTable is:

    1 == 0:TypeDef[2000004], 1:MethodDefOrRef[06000005], 2:MethodDefOrRef[06000002]
    2 == 0:TypeDef[2000005], 1:MethodDefOrRef[06000008], 2:MethodDefOrRef[06000001]
    

    Then you can look in the typedef and methoddef tables, and you'll see that this decodes as:

    in type A the method A::I2.Draw implements the method I2::Draw
    in type B the method B::I1.Draw implements the method I1::Draw
    

    The MethodImpl table is how the CLI represents the notion of "I need to stick something in this slot that is different than what the regular name matching rules would choose". Normally the name matching rules would choose a method called "Draw" to go in that slot, not "I1.Draw" or "I2.Draw".

    You might also want to read section 22.27 of Partition II of the CLI spec.

提交回复
热议问题