C#继承里的【就近原则】
参考博客: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#继承里的【就近原则】的更多相关文章
- 设计原则:里式替换原则(LSP)
系列文章 设计原则:单一职责(SRP) 设计原则:开闭原则(OCP) 设计原则:里式替换原则(LSP) 设计原则:接口隔离原则(ISP) 设计原则:依赖倒置原则(DIP) 何谓高质量代码? 理解RES ...
- css叠加原则,就近原则
<html><head lang="en"> <meta charset="UTF-8"> <title>< ...
- C++ 抽象类一(多继承与赋值兼容性原则)
//多继承与赋值兼容性原则 #include<iostream> using namespace std; class Point{ public: Point(){ a = ; b = ...
- java6大原则之单一职责原则,里式替换原则
单一职责原则:一个接口,一个类,一个方法,最好只做一类事,当然,在真实的项目中,一系列因素下,很难做到单一职责原则,但是针对接口是可以做到的,方法和类要尽量做到 里式替换原则:父类出现的地方,换成子类 ...
- .net学习之继承、里氏替换原则LSP、虚方法、多态、抽象类、Equals方法、接口、装箱拆箱、字符串
1.继承(1)创建子类对象的时候,在子类对象中会为子类对象的字段开辟空间,也会为父类的所有字段开辟空间,只不过父类私有的成员访问不到(2)子类从父类继承父类所有的非私有成员,但是父类的所有字段也会创建 ...
- c++继承构造析构调用原则以及特殊变量处理
一.继承中的构造析构调用原则 1.子类对象在创建时会首先调用父类的构造函数 2.父类构造函数执行结束后,执行子类构造函数 3.当父类构造函数有参数时,需要在子类的初始化列表中显示调用: 4.析构函数调 ...
- css第二篇:样式的特殊性、重要性、继承和层叠
特殊性: 假设有几个不同的规则改变的都是同一个元素的值,那么哪一个规则将会胜出呢?这就得靠特殊值啦,什么是特殊值呢?特殊值的大小呢?如下图: 值越大代表越牛,如1,0,0,0永远大于0,X,X,X ...
- java笔记整理
Java 笔记整理 包含内容 Unix Java 基础, 数据库(Oracle jdbc Hibernate pl/sql), web, JSP, Struts, Ajax Spring, E ...
- 面向对象世界里转转七(Liskov替换原则)
前言:Liskov替换原则是关于继承机制的应用原则,是实现开放封闭原则的具体规范,违反了Liskov原则必然意味着违反了开放封闭原则.因此,有必要对面向对象的继承机制及其基本原则做以探索,来进一步了解 ...
随机推荐
- HBase最佳实践-列族设计优化
本文转自hbase.收藏学习下. 随着大数据的越来越普及,HBase也变得越来越流行.会用HBase现在已经变的并不困难,然而,怎么把它用的更好却并不简单.那怎么定义'用的好'呢?很简单,在保证系统稳 ...
- 第二周:If判断语句程序当中的作用简介
1.If语句的作用: 在我们编写程序时经常会遇到内容判断的问题,比如判断内容的真假或者值的大小分别输出内容的问题 这时就会用到我们的If判断语句了,顾名思义,if在英文单词中意思为如果,在Java中他 ...
- linux journel
http://www.linuxjournal.com/article/8545 http://www.linuxjournal.com/article/8093 http://www.linuxjo ...
- 在 Typescript 2.0 中使用 @types 类型定义
在 Typescript 2.0 中使用 @type 类型定义 基于 Typescript 开发的时候,很麻烦的一个问题就是类型定义.导致在编译的时候,经常会看到一连串的找不到类型的提示.解决的方式经 ...
- hrbrid需要做的
1 返回并刷新 A webveiw push 到 B webview.当由B返回到A时候, A需要刷新页面.
- SNMP开发c#,高薪诚聘,或者外包开发
现在手里有一个IT资产管理方面的项目,主要会用到SNMP协议,要用这个协议进行开发,对网络上的设备进行控制, 现在高薪诚聘高手,或者项目外包合作,有意向的同学们可以联系我,时间紧迫, QQ:39011 ...
- jquery 获取鼠标和元素的坐标点
获取当前鼠标相对img元素的坐标 $('img').mousemove(function(e) { varpositionX=e.pageX-$(this).offset().left; //获取当前 ...
- MySQL数据库(表)的导入导出(备份和还原)
一)在同一个数据库服务器上面进行数据表间的数据导入导出: 1. 如果表tb1和tb2的结构是完全一样的,则使用以下的命令就可以将表tb1中的数据导入到表tb2中: insert into db2.tb ...
- 有一字符串,包含n个字符。写一函数,将此字符串中从第m个字符开始的全部字符复制成为另一个字符串。
[提交][状态][讨论版] 题目描述 有一字符串,包含n个字符.写一函数,将此字符串中从第m个字符开始的全部字符复制成为另一个字符串. 输入 数字n 一行字符串 数字m 输出 从m开始的子串 样例输入 ...
- Nancy+BUI+SQLite自动更新服务端和客户端保护更新程序
写了一个自动更新服务网站和自动更新客户端.基本功能测试完毕.记录下 项目地址:https://github.com/TeemoHQ/AutoUpdate 需求 自动集成,回退版本,守护进程,服务端操作 ...