结合IL和Windbg来看.Net调用继承虚方法的执行顺序

王朝学院·作者佚名  2016-08-27  
宽屏版  字体: |||超大  

先上测试代码:

usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;namespaceTestVirt

{classPRogram

{staticvoidMain(string[] args)

{

A c1=newC();

c1.Foo();

C c2=newC();

c2.Foo();

Console.ReadLine();

}

}classA

{publicvirtualvoidFoo()

{

Console.WriteLine("Call on A.Foo()");

}

}classB : A

{publicoverridevoidFoo()

{

Console.WriteLine("Call on B.Foo()");

}

}classC : B

{publicnewvoidFoo()

{

Console.WriteLine("Call on C.Foo()");

}

}

}

View Code

可能你对C c2 = new C();这个的结果没有什么疑问,但是对A c1 = new C();的结果百思不解。呵呵,我们慢慢来看这个区别,先来看看最终的IL代码是什么样子的:

.method private hidebysig static void Main(string[] args) cil managed

{

.entrypoint

// 代码大小 34 (0x22)

.maxstack 1

.locals init ([0] class TestVirt.A c1,

[1] class TestVirt.C c2)

IL_0000: nop

IL_0001: newobj instance void TestVirt.C::.ctor()

IL_0006: stloc.0

IL_0007: ldloc.0

IL_0008:callvirt instance void TestVirt.A::Foo()IL_000d: nop

IL_000e: newobj instance void TestVirt.C::.ctor()

IL_0013: stloc.1

IL_0014: ldloc.1

IL_0015:callvirt instance void TestVirt.C::Foo()IL_001a: nop

IL_001b: call string [mscorlib]System.Console::ReadLine()

IL_0020: pop

IL_0021: ret

} // end of method Program::Main

根据IL的结果,我们明显可以看到,两次调用不同的地方就在于一个是Call的A的Foo,一个是C的Foo。

但是这里你注意了:我们new的是同样的一个对象,他们具有同样的内存布局。

用WinDbg来看看我们new出来对象的MethodTable是长什么样子的吧:

看到没有,这个C对象的方法表里面同时包括了C自己定义的Foo和上一层次父对象的Foo方法。

结合IL的结果和C对象的方法表的Dump结果,相信看官已经明白为啥两次调用为啥会用不同了吧。

算了,还是简单描述一下吧:首先根据il的结果明显两次调用请求的方法是不同的;其次,你可以看到我们的C对象引用的方法表里面确实有两个Foo方法。

呵呵,这样同样类型的对象对不同方法调用的请求是不是就可以分开了呢?当然是!

PS:可能会有人问:为啥我请求的A.Foo()你这个MethodTable里面没有呢?

脑补下吧哥:被B给override了。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
© 2005- 王朝网络 版权所有