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
interface I1 { void Draw(); }
interface I2 { void Draw(); }
class A : I1, I2
{
// this is just a method in A
public void Minstance() { Console.WriteLine("A::MInstance"); }
// method in A, also implements I1.Draw. May be overridden in
// derived types.
public virtual void Draw() { Console.WriteLine("A::Draw"); }
// implements I2.Draw, accessible on object a of type A via ((I2)a).Draw()
void I2.Draw() { Console.WriteLine("A::I2.Draw"); }
}
class B : A, I1, I2
{
// new method in B, does not override A.Draw, so A.Draw is only
// callable on an object b of type B via ((A)b).Draw(). Types
// derived from B may override this method, but can't override
// A.Draw because it's hidden. Also implements I2.Draw (see notes).
public new virtual void Draw() { Console.WriteLine("B::Draw"); }
// implements I1.Draw, accessible on object b of type B via ((I1)b).Draw()
void I1.Draw() { Console.WriteLine("B::I2.Draw"); }
}
A class that inherits an interface implementation is permitted to re-implement the interface by including it in the base class list.
A re-implementation of an interface follows exactly the same interface mapping rules as an initial implementation of an interface. Thus, the inherited interface mapping has no effect whatsoever on the interface mapping established for the re-implementation of the interface. [Example: In the declarations
interface IControl
{
void Paint();
}
class Control: IControl
{
void IControl.Paint() {…}
}
class MyControl: Control, IControl
{
public void Paint() {}
}
the fact that
Control
mapsIControl.Paint
ontoControl.IControl.Paint
doesn’t affect the reimplementation inMyControl
, which mapsIControl.Paint
ontoMyControl.Paint
. end example]Inherited public member declarations and inherited explicit interface member declarations participate in the interface mapping process for re-implemented interfaces. [Example:
interface IMethods
{
void F();
void G();
void H();
void I();
}
class Base: IMethods
{
void IMethods.F() {}
void IMethods.G() {}
public void H() {}
public void I() {}
}
class Derived: Base, IMethods
{
public void F() {}
void IMethods.H() {}
}
Here, the implementation of
IMethods
inDerived
maps the interface methods ontoDerived.F
,Base.IMethods.G
,Derived.IMethods.H
, andBase.I
. end example]When a class implements an interface, it implicitly also implements all that interface’s base interfaces. Likewise, a re-implementation of an interface is also implicitly a re-implementation of all of the interface’s base interfaces. [Example:
interface IBase
{
void F();
}
interface IDerived: IBase
{
void G();
}
class C: IDerived
{
void IBase.F() {…}
void IDerived.G() {…}
}
class D: C, IDerived
{
public void F() {…}
public void G() {…}
}
Here, the re-implementation of
IDerived
also re-implementsIBase
, mappingIBase.F
ontoD.F
. end example]
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.
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.
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.
From what I understand you're asking, given a subclass with some overridden methods, how to know which method will get called.
basically, there are 3 options I can think of:
I hope I understood your question
edit: a quick note about interfaces: If BaseClass implements IInterface, then SubClass, which derives from BaseClass, already has the implementation of IInterface and does not need to re-implement it. E.g. if there's an IVehicle with an MPH property, and there's a BaseVehicle that implements that, since Car derives from BaseVehicle, Car already has an MPH property
In addition to other answers I am posting a correct answer:
A a = new B();
a.Draw(); //A::Draw
I1 i1 = new A();
i1.Draw(); //A::Draw
I2 i2 = new B();
i2.Draw();// B::Draw
B b = (B) a;
b.Draw();// B::Draw
"Method table"? No, it's just a contract that has to be satisfied. The contracts of I1
and I2
can be satisfied with the same Draw
method, and will be, unless you separate one off with an implicit implementation, as is the case here. Without this, a single Draw
method will be fine.
In all cases, the public Draw
will be called except when the reference is cast to the interface type that is explicitly implemented.
Firstly the explanation of new and virtual
New
e.g.
A a2= new B() (creates a new object using constructor of B, because b is of type A, therefore it works )
and a2.draw() will result in execution of A class draw because the type is A.
Virtual
override