马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
兄弟们,想来你们都看过了昨天的比赛了。我现在的痛苦状跟当时应该差不多。希望本版.net老师不吝赐教,为小弟这一批迷途的羊羔指一条阳光之道!您也知道:学习技术如果只有一个人摸索,那是一件多么痛苦的事情!还有,如果万辛能得名师或长者指点,那又是多么一件幸福和快乐的事情!系列文章目次索引:《你必需晓得的.NET》
本文将先容以下内容:
1.弁言
关于承继,你是不是驾熟就轻,关于承继,你是不是洞若观火。
本文不会商承继的基础观点,我们回回实质,从编译器运转的角度来展现.NET承继中的运转根源,来发明子类对象是怎样完成了对父类成员与办法的承继,以最为大略的示例来展现承继的本色,论述承继机制是怎样被实行的,这关于更好的了解承继,是需要且一定的。
2.剖析
上面起首以一个复杂的植物承继系统为例,来举行申明:
publicabstractclassAnimal
publicabstractvoidShowType();
Console.WriteLine("Animalalwayseat.");
privatestringtype="Bird";
publicoverridevoidShowType()
Console.WriteLine("Typeis{0}",type);
privatestringtype="Chicken";
publicoverridevoidShowType()
Console.WriteLine("Typeis{0}",type);
Console.WriteLine("Coloris{0}",Color);
然后,在测试类中创立各个类对象,因为Animal为笼统类,我们只创立Bird对象和Chicken对象。
publicclassTestInheritance
Chickenchicken=newChicken();
上面我们从编译角度对这一复杂的承继示例举行深切剖析,从而懂得.NET外部是怎样完成我们夸大的承继机制。
(1)我们扼要的剖析一下对象的创立历程:
Birdbird创立的是一个Bird范例的援用,而newBird()完成的是创立Bird对象,分派内存空间和初始化操纵,然后将这个对象赋给bird援用,也就是创建bird援用与Bird对象的联系关系。
(2)我们从承继的角度来剖析在编译器编译期是怎样实行对象的创立历程,由于承继的实质就表现于对象的创立历程。
在此我们以Chicken对象的创立为例,起首是字段,对象一经创立,会起首找到其父类Bird,并为其字段分派存储空间,而Bird也会持续找到其父类Animal,为其分派存储空间,顺次类推直到递回停止,也就是完成System.Object内存分派为止。我们能够在编译器中单步实行的办法来大抵懂得其分派的历程温柔序,因而,对象的创立历程是依照按次完成了对全部父类及其自己字段的内存创立,而且字段的存储按次是由上到下分列,object类的字段排在最后面,其缘故原由是假如父类和子类呈现了同名字段,则在子类对象创立时,编译器会主动以为这是两个分歧的字段而加以区分。
然后,是办法表的创立,必需明白的一点是办法表的创立是类第一次加载到CLR时完成的,在对象创立时只是将其附加成员TypeHandle指向办法列表在LoaderHeap上的地点,将对象与其静态办法列表相干联起来,因而办法表是先于对象而存在的。相似于字段的创立历程,办法表的创立也是父类在先子类在后,缘故原由是不言而喻的,类Chicken天生办法列表时,起首将Bird的一切办法拷贝一份,然后和Chicken自己的办法列表做以对照,假如有覆写的虚办法则以子类办法掩盖同名的父类办法,同时增加子类的新办法,从而创立完成Chicken的办法列表。这类创立历程也是逐层递回到Object类,而且办法列表中也是依照按次分列的,父类在前子类在后,其缘故原由和字段迥然不同,留待读者本人体会。
分离我们的剖析历程,如今将对象创立的历程以复杂的图例来展现其在内存中的分派情况,以下:
从我们的剖析,和下面的对象创立历程可见,对承继的实质我们有了更明白的熟悉,关于以下的成绩就有了明晰分明的谜底:
- 承继是可传送的,子类是对父类的扩大,必需承继父类办法,同时能够增加新办法。
- 子类能够挪用父类办法和字段,而父类不克不及挪用子类办法和字段。
- 虚办法怎样完成覆写操纵,使得父类指针能够指向子类对象成员。
- new关头字在虚办法承继中的阻断感化。
你是不是已找到了了解承继、了解静态编译的不贰秘诀。
3.思索
经由过程下面的报告与剖析,我们基础上对.NET在编译期的完成道理有了大抵的懂得,可是另有以下的成绩,必定会引发必定的困惑,那就是:
这类情形下,bird2.ShowType应当前往甚么值呢?而bird2.type有该是甚么值呢?有两个准绳,是.NET专门用于办理这一成绩的:
- 存眷对象准绳:挪用子类仍是父类的办法,取决于创立的对象是子类对象仍是父类对象,而不是它的援用范例。比方Birdbird2=newChicken()时,我们存眷的是其创立对象为Chicken范例,因而子类将承继父类的字段和办法,大概覆写父类的虚办法,而不必存眷bird2的援用范例是不是为Bird。援用范例分歧的区分决意了分歧的对象在办法表中分歧的会见权限。
注重
依据存眷对象准绳,那末上面的两种情形又该怎样区分呢?
Chickenchicken=newChicken();
依据我们上文的剖析,bird2对象和chicken对象在内存结构上是一样的,不同就在于其援用指针的范例分歧:bird2为Bird范例指针,而chicken为Chicken范例指针。以办法挪用为例,分歧的范例指针在假造办法表中有分歧的附加信息作为标记来区分其会见的地点地区,称为offset。分歧范例的指针只能在其特定地点地区内举行实行,子类掩盖父类时会包管其会见地点地区的分歧性,从而办理了分歧的范例会见具有分歧的会见权限成绩。
- 实行就近准绳:关于同名字段大概办法,编译器是依照其按次查找来援用的,也就是起首会见离它创立比来的字段大概办法,比方上例中的bird2,是Bird范例,因而会起首会见Bird_type(注重编译器是不会从头定名的,在此是为辨别起见),假如type范例设为public,则在此将前往“Bird”值。这也就是为何在对象创立时必需将字段按按次分列,而父类要先于子类编译的缘故原由了。
思索
1.下面我们剖析到bird2.type的值是“Bird”,那末bird2.ShowType()会显现甚么值呢?谜底是“TypeisChicken”,依据本文下面的剖析,想一想究竟为何?
2.关于new关头字在虚办法静态挪用中的阻断感化,也有了更明白的实际基本。在子类办法中,假如标志new关头字,则意味着埋没基类完成,实在就是创立了与父类同名的另外一个办法,在编译中这两个办法处于静态办法表的分歧地点地位,父类办法排在后面,子类办法排在前面。
4.结论
在.NET中,假如创立一个类,则该类老是在承继。这缘于.NET的面向对象特征,一切的范例都终极承继自配合的根System.Object类。可见,承继是.NET运转机制的基本手艺之一,统统皆为对象,统统皆于承继。本文从基本动身,深切实质探究根源,剖析疑问对照判别。关于甚么是承继这个话题,但愿每一个人能从中追求本人的谜底,了解承继、存眷封装、玩转多态是了解面向对象的出发点,但愿本文是这一路程的出发点。
[祝愿]
仅以此篇献给我的先生们:汤文海先生,陈桦先生。
<br>
参考文献
(USA)DonBox,Essential.NET
(中国)虫虫,从编译的角度看对象
|