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原则必然意味着违反了开放封闭原则.因此,有必要对面向对象的继承机制及其基本原则做以探索,来进一步了解 ...
随机推荐
- seo高级指令
1.双引号:完全匹配2.减号(-):代表搜索不包含减号后面的启事的页面3.星号(*):是常用的通配符,百度不支持4.inurl:指令用于搜索查询词出现在URL中的页面5.inanchor:指令返回的结 ...
- iOS开发 - OC - 实现本地数据存储的几种方式一
iOS常用的存储方式介绍 在iOS App开发过程中经常需要操作一些需要持续性保留的数据,比如用户对于App的相关设置.需要在本地缓存的数据等等.本文针对OC中经常使用的一下存储方式做了个整理. 常用 ...
- day10-rabbitmq
安装python rabbitMQ module pip instal pika 发布者: #!/usr/bin/env python #coding:utf8 import pika connect ...
- Django 后台管理设置(admin.py)
上面是两种后台效果图,第一张是默认情况下Django的后台,第二张是稍作修改后的情况,下面记录下作了哪些修改: 代码: class ArticleAdmin(admin.ModelAdmin): li ...
- mysql数据库字符集的设置
my.ini文件中clinet和mysqld中的写法不一样,要注意 [client] port=3306 default-character-set=utf8 [mysqld] ...
- jquery如何在异步方式中给全局变量赋值
定义一个全局全量,例如:var i=0;然后调用jquery的一个函数 $.get(url, function(data) {i=9;})alert(i);得到的是0而不是9. var i=0; ...
- WebStorm注册码
WebStorm注册码User Name:EMBRACE License Key:===== LICENSE BEGIN =====24718-1204201000001h6wzKLpfo3gmjJ8 ...
- MVC执行过程
HttpRuntime中的PR方法1,封装HttpContext2,获取HttpApplication 主要做3件事a,执行本事件时主要调用Init将Global编译得到类型,b,确保Appstart ...
- Access denied for user 'root'@'localhost' (using password:YES)
版权所有,未经博主允许不得转载. 今天发现服务器mysql连接不上,报 can't connect to mysql server on 'root'@'ip' (61) 照例ssh到服务器,发现my ...
- 引用对象的使用和易产生bug的示例
本文属原创,转载请注明出处:http://www.cnblogs.com/robinjava77/p/5481608.html (Robin) QuoteTest(引用对象技巧) import ja ...