深入理解virtual/new/override 这些关键字的意义-阿里云开发者社区

开发者社区> 吞吞吐吐的> 正文

深入理解virtual/new/override 这些关键字的意义

简介:
+关注继续查看
1None.gifpublic class Base
 2ExpandedBlockStart.gif{
 3InBlock.gif    public Base()
 4ExpandedSubBlockStart.gif    {
 5InBlock.gif        Method1();
 6ExpandedSubBlockEnd.gif    }
 7ExpandedSubBlockStart.gif    public virtual void Method1() {
 8InBlock.gif        Console.WriteLine("In Base's Method1()");
 9ExpandedSubBlockEnd.gif    }
10ExpandedBlockEnd.gif}

11None.gifpublic class Derived: Base
12ExpandedBlockStart.gif{
13InBlock.gif    private int value;
14InBlock.gif    public Derived()
15ExpandedSubBlockStart.gif    {
16InBlock.gif        value = 42;
17ExpandedSubBlockEnd.gif    }
18InBlock.gif
19InBlock.gif    public override void Method1()
20ExpandedSubBlockStart.gif    {
21InBlock.gif        if (value == 42) Console.WriteLine("value == 42, all is good");
22InBlock.gif        else Console.WriteLine("value != 42, what is wrong?");
23ExpandedSubBlockEnd.gif    }
24ExpandedBlockEnd.gif}

Derived dev = new Derived()
问:屏幕的输出是多少?

输出:value != 42, what is wrong?
原因:在执行Derived的构造函数之前,要先执行父类Base的构造函数,而在Base的构造函数中,调用了虚方法Method1,此时CLR会监测到this的运行时类型为Derived,因此在调用(this.)Method1时,实际调用的是Derived.Method1()方法;而此时还没有执行父类Derived构造函数,因此value还没有赋值(默认为0),因此要执行语句:Console.WriteLine("value != 42, what is wrong?");


下面详细介绍virtual/new/override 这些关键字的意义

 

1. virtual:当一个方法为是virtual方法,CLR在调用该方法时,会监测此时this的运行时类型(可以通过this.GetType().ToString()查看),然后调用运行时类型上的重写(override)方法。CLR在调用virtual方法时,产生callvirt指令(IL),该指令在被JIT编译成汇编语言时,产生三条汇编指令(可以在“命令”窗口中输入disasm来查看编译后的IA-32代码):
mov ecx,esi; //将目标对象的引用(在这里是this)存储在IA-32 ecx寄存器中;
mov eax,dword ptr[ecx];//针对虚方法调用的指令,将对象的类型句柄存储在eax寄存器中;
call dword ptr [eax+offset];通过对象的类型句柄和方法在方法表中的偏移量来定位目标方法的实际地址;
而对于非virtual方法,CLR在调用该方法时,产生call指令(IL),该指令在被JIT编译成会被语言时,只产生两条汇编指令(相比callvirt而言,call指令不需在运行时检测对象的运行时类型):
mov ecx,esi;//把目标对象的引用放进ecx寄存器
call methodAddress;//直接调用methodAddress指向的目标方法 

2. new:子类在继承了父类后,可以用new来隐藏父类中的方法。此时,在子类的方法表(Mathod Table)中,仍然保留父类中该方法的方法槽(slot,.net对象模型中,类型中的每个方法在方法表中都占用一个方法槽)。因此,我们仍然可以将子类的引用强制转换成父类的引用,来调用父类中的非virtual或virtual方法。例如:下面第四题中的代码,如果我们将override改成new,则最终结果是:执行父类Base的Method1,输出“In Base's Method1()”。

3. override:子类在继承了父类后,可以用override来重写父类中的方法。此时,在子类的方法表中,不再保留父类中该方法的方法槽。当我们将子类的引用强制转换成父类的引用,来试图调用父类中的virtual方法时,实际上是不能执行成功的,因为CLR检测到此时对象的运行时类型是子类类型,于是将调用分派(dispatch)到子类的方法上。例如:下面第四题中的代码,在父类Base中调用子类中已重写的Method1方法,CLR检测到此时this的运行时类型为Derived,因此仍然会调用Derived中重写的Method1。

 

例如:上面中的例子,可以用VS2005SOS扩展来分析Base/Derived的对象模型(调试过程中,在“即使窗口”中输入的命令用蓝色显示,其余的为输出信息)(有关SOS的使用,可以参考我以前写的:《使用SOS - 在Visual Studio中启用非托管代码调试来支持本机代码调试 》)。可以看出,在使用override的情况下,子类的方法表中只有一个Method1方法槽(下面以红色显示的部分),子类Derived覆盖重写了父类BaseMethod1方法;而在使用new的情况下,子类的方法表中有两个Method1方法槽,子类Derived仅仅只是对外隐藏了父类Base中的Method1

1. override

!dumpheap -type Derived
PDB symbol for mscorwks.dll not loaded
 Address       MT     Size
013f8828 00a531f0       12     
total 1 objects
Statistics:
      MT    Count    TotalSize Class Name
00a531f0        1           12 Bingosoft.Training2007.CSharp.Derived
Total 1 objects 

!DumpMT -MD 00a531f0
EEClass: 00a513f8
Module: 00a52c14
Name: Bingosoft.Training2007.CSharp.Derived
mdToken: 02000004 (H:\Training\DotNetExam\DotNetExam\bin\Debug\DotNetExam.exe)
BaseSize: 0xc
ComponentSize: 0x0
Number of IFaces in IFaceMap: 0
Slots in VTable: 6
--------------------------------------
MethodDesc Table
   Entry MethodDesc      JIT Name
79354bec   7913bd48   PreJIT System.Object.ToString()
793539c0   7913bd50   PreJIT System.Object.Equals(System.Object)
793539b0   7913bd68   PreJIT System.Object.GetHashCode()
7934a4c0   7913bd70   PreJIT System.Object.Finalize()
00a53248   00a531e8      JIT Bingosoft.Training2007.CSharp.Derived.Method1()
00a53238   00a531e0      JIT Bingosoft.Training2007.CSharp.Derived..ctor()

 

2. 如果我们将Derived类中的override改成new,得到的分析结果如下:

!dumpheap -type Derived
PDB symbol for mscorwks.dll not loaded
 Address       MT     Size
013f8828 00a531f0       12     
total 1 objects
Statistics:
      MT    Count    TotalSize Class Name
00a531f0        1           12 Bingosoft.Training2007.CSharp.Derived
Total 1 objects

!DumpMT -MD 00a531f0
EEClass: 00a513f8
Module: 00a52c14
Name: Bingosoft.Training2007.CSharp.Derived
mdToken: 02000004 (H:\Training\DotNetExam\DotNetExam\bin\Debug\DotNetExam.exe)
BaseSize: 0xc
ComponentSize: 0x0
Number of IFaces in IFaceMap: 0
Slots in VTable: 7
--------------------------------------
MethodDesc Table
   Entry MethodDesc      JIT Name
79354bec   7913bd48   PreJIT System.Object.ToString()
793539c0   7913bd50   PreJIT System.Object.Equals(System.Object)
793539b0   7913bd68   PreJIT System.Object.GetHashCode()
7934a4c0   7913bd70   PreJIT System.Object.Finalize()
00a531b0   00a53150      JIT Bingosoft.Training2007.CSharp.Base.Method1()
00a53240   00a531e0      JIT Bingosoft.Training2007.CSharp.Derived..ctor()
00a53250   00a531e8     NONE Bingosoft.Training2007.CSharp.Derived.Method1()



 本文转自Silent Void博客园博客,原文链接:http://www.cnblogs.com/happyhippy/archive/2007/07/22/827583.html,如需转载请自行联系原作者

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
10053 0
浅谈C#中new、override、virtual关键字的区别
<p style="margin:10px auto; color:rgb(35,35,35); line-height:25px"><span style="font-size:14px; font-family:Verdana,Arial,helvetica,sans-seriff; white-space:pre"></span><span style="font-family:Ka
1751 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
10882 0
阿里云服务器安全组设置内网互通的方法
虽然0.0.0.0/0使用非常方便,但是发现很多同学使用它来做内网互通,这是有安全风险的,实例有可能会在经典网络被内网IP访问到。下面介绍一下四种安全的内网互联设置方法。 购买前请先:领取阿里云幸运券,有很多优惠,可到下文中领取。
11816 0
【转】深入理解C++中的mutable关键字
mutalbe的中文意思是“可变的,易变的”,跟constant(既C++中的const)是反义词。  在C++中,mutable也是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中。
771 0
4852
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载