我们创建如下的三层继承层次类。

   public abstract class Animal
{
public abstract void ShowType();
} public class Bird : Animal
{
private string type = "Bird";
public override void ShowType()
{
Console.WriteLine("Type is {0}", type);
}
}
public class Chicken : Bird
{
private string type = "Chicken";
public override void ShowType()
{
Console.WriteLine("Type is {0}", type);
}
}

(1)简析对象创建过程

Bird bird=new Bird();
Bird bird创建的是一个Bird类型的引用,而new Bird()完成的是创建Bird对象,分配内存空间和初始化操作,然后将这个对象引用赋给bird变量,用示例图来表示情况就是这样:

(2)分析CLR是如何执行对象的创建过程的?

字段是如何创建的:

我们以基类Chicken的对象创建为例,我们幻想在托管堆中,有一块叫GC Heap的空间,用于存储Chicken对象的字段,对象一经创建,CLR会找到其父类Bird,并为其字段在GCHeap分配了空间用于存储,你没听错,实例化Chicken对象也要将其基类的字段分配存储空间,然后Bird类又会继续找到其父类Animal直到System.Object,我们在各个类的字段上添加断点进行调试,Chicken chicken=new Chicken(); 创建对象的时候,会首先进到1,依次是2、3;这也证明了实例化子类也要为父类字段分配存储空间;

所以,对象的创建过程是按照顺序完成了对整个父类及其本身字段的内存创建,并且字段的存储顺序是按照类的高低层次来的,最高层的类排在最前面,如果父类和子类出现了同名字段,则子类创建的时候,编译器会自动加与区别这是两个不同的字段,比如:type字段,Bird_type、Chicken_type 这样子,到了这一步,用示例图来表示是这样子的:

方法表:

方法表是在什么时候创建的:

类第一次加载到AppDomain时完成的,留意到上图中的Type_Handle没有,在对象创建时,将附加成员Type_Handle指向方法表在LoaderHeap(加载堆)上的地址,也就是先有方法表再有对象,这样就完成了对象与动态方法表的关联操作。

方法表是如何生成的?

方法表的创建跟字段的创建过程类似,也是逐层递归知道Object类,Chicken子类生成方法列表时,先将Bird类所有虚方法复制一份,如果Chicken类有override父类的虚方法,则子类方法覆盖相应的虚方法,同时添加子类新方法,并且顺序也是父类在前,子类在后,这个顺序很重要,在后面一节方法调用的时候,就会跟这个顺序有关,也是很多面试题的考点。
总结:方法表 = 父类没有被override的方法 + 子类子类的方法,所以到了这一步,用示例图来表示应该是这样:

Q:

好了,问题来了,那当我们以Bird bird=new Chicken(); 创建对象的时候,bird.type 等于什么?bird.ShowType()又输出什么?本书的作者AnyTao列举了两个原则,当我们熟知这两个原则和理解字段的创建、方法表的创建,就会明白为什么会出现这样的输出结果了。
○ 关注对象原则:Bird bird=new Chicken(); 我们关注的应该是new的是什么类,也就是关注创建的是Chicken类型的对象;
○ 执行就近原则:首先访问离它创建最近的字段或者方法;
     书中关于这段输出结果的原因和结果篇章比较少,我不知道为什么AnyTao没有继续讲解下去而是作为思考抛给了读者,为此,我研究了一下,而且编写DEMO输出结果,在此我说说自己的见解,如果说得不妥请观众们评论矫正。
Bird bird=new Chicken();

bird.type的结果什么?

根据内存分布原则,此时栈上是Bird类型的变量引用,而托管堆上的是Chicken类型的对象,内存分布图应该是这样子的。

此时托管堆上字段有两个,分别是Bird_type、Chicken_type(注意:编译器不会重新命名,此处为了便于理解),那应该输出哪个才对?根据执行就近原则,bird.type 其中的bird是Bird类型的变量引用,所以,首先会访问 Bird_type="Bird";

bird.ShowType()的结果是什么?

我的理解是,此处也应该执行就近原则,但是Bird类中的ShowType()方法已经在Chicken类中被Override了,所以在内存分布图中的方法表上,只能找到Chicken.ShowType();而在Chicken类中, private string type = "Chicken"; type字段被赋值,所以输出结果为“Type is Chicken”

总结:

通过上面的文字,我们可以理解继承的本质,无论把Bird.type设置为Public还是Private,父类的字段都早就已经存在子类对象所在托管堆的内存分配空间中了,只是设置为Private的时候,子类对象无法访问而已。 短短的几页书,看的时间虽短,但是整理成章花费的时间却不少,看看电脑屏幕右下角的时钟,已经花费了两个小时,包括作图、写代码验证书中结果、思考AnyTao给出的问题等,但是这一切我都觉得很值,从来没有如此的深入研究.NET底层的知识,刚毕业面试的时候回答这类问题总是瞎蒙,深入原理、庖丁解牛乃程序员立足之根本!

读《你必须知道的.NET》继承本质论 Bird bird=new Chicken()的更多相关文章

  1. 必须知道的.net(继承)

    1.继承定义:就是面向对象中类与类之间的一种关系.通过继承,使得子类具有父类的属性和方法,同时子类也可以通过加入新的属性和方法或者修改父类的属性和方法建立新的类层次. 2.CLR支持实现单继承和接口多 ...

  2. 读<你必须知道的.NET>IL指令笔记

    IL指令笔记: 1.newObj和initObj MSDN解释:newObj用于分配和初始化对象,而initObj用户初始化值类型 newObj解释: (1):从托管堆分配指定类型所需要的全部内存空间 ...

  3. 解惑《你必须知道的.net》——C#继承关系中【方发表】的创建和调用

    前言: 现在正在读<你必须知道的.net>(第二版)一书,看到IL语言那一章,将call.callvirt和calli时候,书中举了一个例子,是一个三层继承的例子,我一开始看的时候就有点懵 ...

  4. [你必须知道的.NET]第二十七回:interface到底继承于object吗?

    发布日期:2009.03.05 作者:Anytao © 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. 说在,开篇之前 在.NET世界里,我们常常听到的一句话莫过于“S ...

  5. [你必须知道的.NET] 第八回:品味类型---值类型与引用类型(上)-内存有理

    原文地址:http://kb.cnblogs.com/page/42318/ 系列文章导航: [你必须知道的.NET] 开篇有益 [你必须知道的.NET] 第一回:恩怨情仇:is和as [你必须知道的 ...

  6. [你必须知道的.NET]第二十回:学习方法论

    说在,开篇之前 本文,源自我回答刚毕业朋友关于.NET学习疑惑的回复邮件. 本文,其实早计划在<你必须知道的.NET>写作之初的后记部分,但是因为个中原因未能如愿,算是补上本书的遗憾之一. ...

  7. [你必须知道的.NET]第二十六回:认识元数据和IL(下)

    发布日期:2009.03.04 作者:Anytao © 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. 说在,开篇之前 书接上回: 第二十四回:认识元数据和IL(上), ...

  8. [你必须知道的.NET]第二十五回:认识元数据和IL(中)

    发布日期:2009.02.25 作者:Anytao © 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. 说在,开篇之前 书接上回[第二十四回:认识元数据和IL(上)], ...

  9. C语言学习书籍推荐《你必须知道的495个C语言问题》

    萨米特 (Steve summit) (作者), 孙云 (译者), 朱群英 (译者) 下载地址:点我 <你必须知道的495个C语言问题>以问答的形式组织内容,讨论了学习或使用C语言的过程中 ...

  10. 《你必须知道的.NET》读书笔记一:小OO有大智慧

    此篇已收录至<你必须知道的.Net>读书笔记目录贴,点击访问该目录可以获取更多内容. 一.对象  (1)出生:系统首先会在内存中分配一定的存储空间,然后初始化其附加成员,调用构造函数执行初 ...

随机推荐

  1. 测试Python代码

    作为程序员,懂得测试,这是必须的职业技能.很遗憾,我以前从未意识到这点,因此经历了很多叫苦不迭的开发生涯.当然了,期望每个人都成为测试高手也是不可能的,但是最基本的单元测试啥的是不惜的,尤其是现在中小 ...

  2. 一段防盗连的PHP代码

    $ADMIN[defaulturl] = http://www.163.com/404.htm;  //盗链返回的地址  $okaysites = array("http://www.163 ...

  3. C语言 malloc、calloc、realloc的区别

    三个函数的申明分别是: void* malloc(unsigned size); void* realloc(void* ptr, unsigned newsize); void* calloc(si ...

  4. [INS-32025] 所选安装与指定 Oracle 主目录中已安装的软件冲突

    windows server 2008 r2 enterprise下的解决办法为:删除C:\Program Files (x86)\Oracle\Inventory\ContentsXML目录下的in ...

  5. Delphi 7学习开发控件

    我们知道使用Delphi快速开发,很大的一方面就是其强大的VCL控件,另外丰富的第三方控件也使得Delphi程序员更加快速的开发出所需要的程序.在此不特别介绍一些概念,只记录自己学习开发控件的步骤.假 ...

  6. 常见算法是js实现汇总(转载)

    常见算法是js实现汇总 /*去重*/ <script> function delRepeat(arr){ var newArray=new Array(); var len=arr.len ...

  7. ZERO 笔试

    1.大多考到了 计算机网络 tcpip  和  操作系统 多线程的知识  直接 懵逼 2.  考到了 递归的全排列 #include<iostream> using namespace s ...

  8. flash builder4.7bug

    flash builder4.7项目,swc中的button实例出来有bug,解决办法: 1,把button都改成movieclip. 2,改用swf做资源.

  9. 技术英文单词贴--G

    G generator 发电机,发生器,生产者

  10. 我发现调用boostrap的弹框

    在引用了boostrap.js和boostrap.css之后 本来boostrap是基于jQuery的.但是我们的项目里没有用jQuery,而是用的zepto. 调用boostrap的弹框有两种方式: ...