有关虚拟/新...加上界面的更多信息!
昨天我发布了一个有关 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 的方法。但如果我们找不到它怎么办?在这些情况下我应该如何处理?为了成功解决这些问题,我是否应该了解任何经验法则?
抱歉这个问题太无聊了,但我真的需要解开我头上的这个结;)
干杯!
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 "boxes", i lost touch with what's really happening to the type's method tables. For instance:
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();
}
}
}
The question thats asked on this exercise is: Fill in the types' method tables according to the code, and explain the output generated by running the Main().
My answer was:
In type A we have 3 methods: MInstance(), Draw()- the A::Draw version - and I2::Draw
In type B we have 4 methods: MInstance from A, B::Draw, I1::Draw and I2::Draw
I'm not very confident about my answer, and thats why i'm posting this question. When we implement interfaces, it's created a new slot on the method table for the methods of said interface? shouldnt we be implementing I2::Draw too in class A?
Likewise, when we call a method using an interface variable (like i1.Draw()) i understand we're on dynamic dispatch, and therefore we should look at the type of the object being held by the variable(type A in that case) and search A's method table for a method called specifically I1.Draw. But what if we don't find it? How should i proceed in these cases? Is there any rule of thumb i should know about in order to successfully tackle these issues?
Sorry for being so boring with this question, but i really need to untie this knot on my head ;)
Cheers!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
好问题。
思考这个问题的方法是:接口有自己的一组插槽。需要一个实现接口的类来填充这些槽。
现在请记住,重载决策的工作是根据类型和参数选择槽。没有参数,因此编译器只有要关闭的类型。
编译器生成的代码显示“在运行时调用所选槽中的任何方法”。
总结:
如果您对其实现方式感兴趣,请使用 ILDASM 反汇编您的程序,然后查看元数据表 25 (0x19),即 MethodImpl 表。该程序的 MethodImplTable 是:
然后您可以查看 typedef 和 methoddef 表,您将看到其解码为:
MethodImpl 表是 CLI 表示“我需要在这个插槽中粘贴与以下内容不同的东西”这一概念的方式:常规名称匹配规则会选择什么”。通常,名称匹配规则会选择一个名为“Draw”的方法进入该插槽,而不是“I1.Draw”或“I2.Draw”。
您可能还想阅读 CLI 规范第 II 部分的第 22.27 节。
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:
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:
Then you can look in the typedef and methoddef tables, and you'll see that this decodes as:
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.
据我了解,您要问的是,给定一个具有一些重写方法的子类,如何知道将调用哪个方法。
基本上,我能想到 3 个选项:
我希望我理解您的问题
编辑:有关接口的快速说明:如果BaseClass实现了IInterface,那么从BaseClass派生的SubClass就已经有了IInterface的实现,不需要重新实现。例如,如果有一个具有 MPH 属性的 IVehicle,并且有一个实现该属性的 BaseVehicle,则由于 Car 派生自 BaseVehicle,所以 Car 已经具有 MPH 属性
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
“方法表”?不,这只是一个必须履行的合同。
I1
和I2
的契约可以使用相同的Draw
方法来满足,并且除非您使用隐式实现将其分开,就像这里的情况一样。如果没有这个,单个Draw
方法就可以了。在所有情况下,当引用强制转换为显式实现的接口类型时,都会调用公共
Draw
例外。"Method table"? No, it's just a contract that has to be satisfied. The contracts of
I1
andI2
can be satisfied with the sameDraw
method, and will be, unless you separate one off with an implicit implementation, as is the case here. Without this, a singleDraw
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.注释和参考:为此,我必须回到标准 (ECMA-334),它可以在 §20.4.4 接口重新实现中找到:
Notes and reference: I had to go back to the standard (ECMA-334) for this, and it's found in §20.4.4 Interface Re-implementation:
除了其他答案之外,我还发布了正确答案:
In addition to other answers I am posting a correct answer:
首先解释一下 new 和 virtual
New
例如
Virtual
override
Firstly the explanation of new and virtual
New
e.g.
Virtual
override