昨天我发布了一个关于new / virtual / override关键字的问题,我从你的答案中学到了很多东西。但我仍然有些怀疑。
在所有“盒子”之间,我与类型的方法表中真正发生的事情失去联系。例如:
interface I1 { void Draw(); }
interface I2 { void Draw(); }
class A : I1, I2
{
public void Minstance() { Console.WriteLine("A::MInstance"); }
public virtual void Draw() { Console.WriteLine("A::Draw"); }
void I2.Draw() { Console.WriteLine("A::I2.Draw"); }
}
class B : A, I1, I2
{
public new virtual void Draw() { Console.WriteLine("B::Draw"); }
void I1.Draw() { Console.WriteLine("B::I1.Draw"); }
}
class Test
{
public static void Main()
{
A a = new B();
a.Draw();
I1 i1 = new A();
i1.Draw();
I2 i2 = new B();
i2.Draw();
B b = (B)a;
b.Draw();
}
}
}
本练习中提到的问题是:根据代码填写类型的方法表,并解释运行Main()生成的输出。
我的回答是: 在类型A中,我们有3个方法:MInstance(),Draw() - A :: Draw版本 - 和I2 :: Draw 在类型B中,我们有4个方法:来自A的MInstance,B :: Draw,I1 :: Draw和I2 :: Draw
我对自己的回答并不是很有信心,这也就是我发布这个问题的原因。当我们实现接口时,它在方法表上为所述接口的方法创建了一个新槽?我们不应该在A类中实现I2 :: Draw吗?
同样,当我们使用接口变量(如i1.Draw())调用方法时,我理解我们处于动态调度,因此我们应该查看变量所持有的对象的类型(类型A在那种情况下)并在A的方法表中搜索专门调用I1.Draw的方法。但是,如果我们找不到它呢?我应该如何处理这些案件?为了成功解决这些问题,我应该知道任何经验法则吗?
很抱歉这个问题很无聊,但我真的需要解开这个问题;)
干杯!
答案 0 :(得分:5)
好问题。
考虑这个问题的方法是:接口获得自己的一组插槽。需要一个实现接口的类来填充这些插槽。
现在请记住,重载解析的工作是根据类型和参数选择插槽。没有参数,因此编译器只有类型可以使用。
编译器生成的代码“在运行时调用所选插槽中的任何方法。”
总结:
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
如果您对如何实现它感兴趣,请使用ILDASM反汇编您的程序,然后查看元数据表25(0x19),MethodImpl表。该程序的MethodImplTable是:
1 == 0:TypeDef[2000004], 1:MethodDefOrRef[06000005], 2:MethodDefOrRef[06000002]
2 == 0:TypeDef[2000005], 1:MethodDefOrRef[06000008], 2:MethodDefOrRef[06000001]
然后你可以查看typedef和methoddef表,你会看到这个解码为:
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
MethodImpl表是CLI如何表示“我需要在此插槽中粘贴某些内容与常规名称匹配规则选择的内容不同”的概念。通常,名称匹配规则会选择一个名为“Draw”的方法进入该槽,而不是“I1.Draw”或“I2.Draw”。
您可能还想阅读CLI规范的第II部分第22.27节。
答案 1 :(得分:4)
根据我的理解,你会问,给定一个带有一些重写方法的子类,如何知道将调用哪个方法。
基本上,我可以想到3种选择:我希望我理解你的问题
编辑关于接口的快速说明:如果BaseClass实现了IInterface,那么派生自BaseClass的SubClass已经具有IInterface的实现,并且不需要重新实现它。例如。如果有一个带有MPH属性的IVehicle,并且有一个实现它的BaseVehicle,因为Car派生自BaseVehicle,Car 已经一个MPH属性
答案 2 :(得分:1)
“方法表”?不,这只是一份必须满足的合同。 I1
和I2
的合同可以使用相同的Draw
方法来满足,并且除非您使用隐式实现将其中一个分开,否则就像这里的情况一样。如果没有这个,单个Draw
方法就可以了。
在所有情况下,当引用转换为明确实现的接口类型时,公共Draw
将被称为,但除外。
答案 3 :(得分:1)
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"); }
}
允许继承接口实现的类通过将其包含在基类列表中来重新实现接口。
接口的重新实现遵循与接口的初始实现完全相同的接口映射规则。因此,继承的接口映射对于为接口的重新实现而建立的接口映射没有任何影响。 [示例:在声明中
interface IControl
{
void Paint();
}
class Control: IControl
{
void IControl.Paint() {…}
}
class MyControl: Control, IControl
{
public void Paint() {}
}
Control
将IControl.Paint
映射到Control.IControl.Paint
并不会影响MyControl
中重新实现的事实,IControl.Paint
将MyControl.Paint
映射到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() {} }
。 结束示例]继承的公共成员声明和继承的显式接口成员声明参与重新实现的接口的接口映射过程。 [实施例:
IMethods
此处,
Derived
中Derived.F
的实施将界面方法映射到Base.IMethods.G
,Derived.IMethods.H
,Base.I
和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() {…} }
。 结束示例]当一个类实现一个接口时,它隐式地也实现了所有接口的基接口。同样,接口的重新实现也隐含地是所有接口的基接口的重新实现。 [实施例:
IDerived
此处,
IBase
的重新实施也会重新实现IBase.F
,将D.F
映射到{{1}}。 结束示例]
答案 4 :(得分:0)
除了其他答案,我发布了一个正确答案:
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
答案 5 :(得分:0)
首先解释新的和虚拟的
新
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.
虚拟
倍率