参考博客:http://www.cnblogs.com/waynechan/p/3570702.html#2883966

参考书籍:《你必须知道的.NET》,作者王涛http://www.cnblogs.com/anytao/

用三个类来描述C#继承中的就近原则:Animal类,Bird类,Chicken类

1.类关系

类之间的关系如下:

    public abstract class Animal
    {
        private string AnimalField = "";//某个字段

        public abstract void ShowType();//显示类型

        public void Eat()
        {
            Console.WriteLine("Animal Can Eat!");
        }
    }
    public class Bird : Animal
    {
        public string Type = "BirdType";
        public override void ShowType()
        {
            Console.WriteLine("this type is {0}", Type);
        }
        private string color;
        public string Color
        {
            get { return color; }
            set { color = value; }
        }
    }
    public class Chicken : Bird
    {
        private new string Type = "ChickenType";
        public override void ShowType()
        {
            Console.WriteLine("this type is {0}", Type);
        }
        public void ShowColor()
        {
            Console.WriteLine("this color is {0}", Color);
        }
    }

2.测试

执行如下代码的输出结果会是什么?

    static void Main(string[] args)
        {
            Bird bird = new Chicken();
            Console.WriteLine(bird.Type);   //bird.Type是什么?
            bird.ShowType();                //bird.Showtype()是什么?
            Console.ReadKey();
        }

3.结果

输出结果是

4.原因分析

首先介绍两个C#中对象的原则

○ 关注对象原则:Bird bird=new Chicken(); 我们关注的应该是new的是什么类,也就是关注创建的是Chicken类型的对象;
○ 执行就近原则:首先访问离它创建最近的字段或者方法;

  4.1 根据内存分布原则,在Bird bird=new Chicken();时,栈上存储的是Bird类型的变量引用,而托管堆上的是Chicken类型的对象,内存分布图应该是这样子的。

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

也就是说,在创建bird这个对象的时候,它第一时间找到的Type字段是Bird类中的字段,即BirdType,所以不管最终new的对象是Bird类的对象还是其子类的对象,bird.Type输出的内容都是BirdType,这个就是符合执行就近原则。

  4.2 由于Bird类中的ShowType()方法已经在Chicken类中被重写了,所以在内存分布图中的方法表上,只能找到Chicken.ShowType();而在Chicken类中, private string type = "Chicken"; type字段被赋值,所以输出结果为“this type is ChickenType”。

5.拓展

 5.1对象的创建过程

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

  5.1方法表 

  • 方法表是什么时候创建的? 

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

  • 方法表是如何生成的?

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

总结:方法表 = 父类没有被override的方法 + 子类子类的方法,所以到了这一步,用示例图来表示应该是这样:

6.总结

  我们可以理解继承的本质,无论把Bird.type设置为Public还是Private,父类的字段都早就已经存在子类对象所在托管堆的内存分配空间中了,只是设置为Private的时候,子类对象无法访问而已。

  博客图解来源于http://www.cnblogs.com/waynechan/p/3570702.html#2883966

C#继承里的【就近原则】的更多相关文章

  1. 设计原则:里式替换原则(LSP)

    系列文章 设计原则:单一职责(SRP) 设计原则:开闭原则(OCP) 设计原则:里式替换原则(LSP) 设计原则:接口隔离原则(ISP) 设计原则:依赖倒置原则(DIP) 何谓高质量代码? 理解RES ...

  2. css叠加原则,就近原则

    <html><head lang="en"> <meta charset="UTF-8"> <title>< ...

  3. C++ 抽象类一(多继承与赋值兼容性原则)

    //多继承与赋值兼容性原则 #include<iostream> using namespace std; class Point{ public: Point(){ a = ; b = ...

  4. java6大原则之单一职责原则,里式替换原则

    单一职责原则:一个接口,一个类,一个方法,最好只做一类事,当然,在真实的项目中,一系列因素下,很难做到单一职责原则,但是针对接口是可以做到的,方法和类要尽量做到 里式替换原则:父类出现的地方,换成子类 ...

  5. .net学习之继承、里氏替换原则LSP、虚方法、多态、抽象类、Equals方法、接口、装箱拆箱、字符串

    1.继承(1)创建子类对象的时候,在子类对象中会为子类对象的字段开辟空间,也会为父类的所有字段开辟空间,只不过父类私有的成员访问不到(2)子类从父类继承父类所有的非私有成员,但是父类的所有字段也会创建 ...

  6. c++继承构造析构调用原则以及特殊变量处理

    一.继承中的构造析构调用原则 1.子类对象在创建时会首先调用父类的构造函数 2.父类构造函数执行结束后,执行子类构造函数 3.当父类构造函数有参数时,需要在子类的初始化列表中显示调用: 4.析构函数调 ...

  7. css第二篇:样式的特殊性、重要性、继承和层叠

    特殊性:   假设有几个不同的规则改变的都是同一个元素的值,那么哪一个规则将会胜出呢?这就得靠特殊值啦,什么是特殊值呢?特殊值的大小呢?如下图: 值越大代表越牛,如1,0,0,0永远大于0,X,X,X ...

  8. java笔记整理

    Java 笔记整理 包含内容     Unix Java 基础, 数据库(Oracle jdbc Hibernate pl/sql), web, JSP, Struts, Ajax Spring, E ...

  9. 面向对象世界里转转七(Liskov替换原则)

    前言:Liskov替换原则是关于继承机制的应用原则,是实现开放封闭原则的具体规范,违反了Liskov原则必然意味着违反了开放封闭原则.因此,有必要对面向对象的继承机制及其基本原则做以探索,来进一步了解 ...

随机推荐

  1. javascript中日期格式与时间戳之间的转化

    日期格式与时间戳之间的转化 一:日期格式转化为时间戳 function timeTodate(date) { var new_str = date.replace(/:/g,'-'); new_str ...

  2. 浏览器内核控制Meta标签

    国内的主流浏览器都是双核浏览器:基于Webkit内核用于常用网站的高速浏览.基于IE的内核用于兼容网银.旧版网站.以360的几款浏览器为例,我们优先通过Webkit内核渲染主流的网站,只有小量的网站通 ...

  3. Activiti开启SQL Log

    log4j.logger.org.activiti.engine.impl.persistence.entity=trace

  4. Python-Numpy函数-tile函数

    tile函数位于python模块 numpy.lib.shape_base中,他的功能是重复某个数组.比如tile(A,n),功能是将数组A重复n次,构成一个新的数组,我们还是使用具体的例子来说明问题 ...

  5. C#动态编译代码,执行一个代码片段,或者从指定文件中加载某个接口的实现类

    在项目进行中有时候会需要配置一些复杂的表达式,在程序运行的时候执行表达式,根据结果执行相应的操作,简单写了一个类Expression,利用.net的动态编译技术实现,代码如下: public clas ...

  6. UNIX历史和标准

    1969年,bell实验室 Ken Thompson在小型机上首次实现了UNIX系统 1979年,加州伯克利分校发布了UNIX发布版--BSD, 随着AT&T不在对电信市场形成垄断,该公司被允 ...

  7. c#扩展出MapReduce方法

    MapReduce方法主体: public static IDictionary<TKey, TResult> MapReduce<TInput, TKey, TValue, TRe ...

  8. IIS 发布添加网站错误:HTTP 错误 500.21 - Internal Server Error 解决方案

    原因:在安装Framework v4.0之后,再启用IIS,导致Framework没有完全安装 解决:开始->所有程序->附件->鼠标右键点击“命令提示符”->以管理员身份运行 ...

  9. session保存用户信息

    前台jsp-------------- <%@ page import="com.manager.form.User" %><%User user = reque ...

  10. 1.3 Makefile 工程管理

    1. 为什么得用Makefile 单步命令生成led.bin [root@cfm880 lesson1]# cd .. [root@cfm880 Part1]# mkdir lesson3 [root ...