目录:

【C#小知识】C#中一些易混淆概念总结--------数据类型存储位置,方法调用,out和ref参数的使用

【C#小知识】C#中一些易混淆概念总结(二)--------构造函数,this关键字,部分类,枚举

【C#小知识】C#中一些易混淆概念总结(三)--------结构,GC回收,静态成员,静态类

【C#小知识】C#中一些易混淆概念总结(四)---------解析Console.WriteLine()

----------------------------------分割线--------------------------------------

这次主要分享的内容是关于继承的知识。

首先,我们先来看看继承;

既然有继承,就要有父类和子类,来看下面的一段代码:

class Person
{
private int nAge;
protected string strName;
double douHeight;
public string strEateType; public void Hello()
{
Console.WriteLine("我可以说Hello!");
}
public void Run()
{
Console.WriteLine("我可以跑!");
}
} class Student : Person
{ }

然后我在Main()函数中实例化子类的对象,代码如下:

  static void Main(string[] args)
{
Student stu1 = new Student();
}

那么在这个过程中内存中发生了些什么呢?

我们先来看misl的中间代码,看看那能发现些什么

由此我们可以发现子类继承了父类的所有成员包括Private和Protect,并为这些成员开辟了空间来存储。

我们再来实例化我们的子类,然后访问父类的字段和方法,会发现,如下的现象

所以虽然子类为父类的所有成员在堆中都开辟了空间,但是父类的私有成员(Private)子类访问不到,

而受保护的成员(protected)可以通过实例化对象访问的到。

所以在内存中的情况如下图:

看下面的代码,我们来探究一下在子类中this关键字和base关键字所访问的类的成员有哪些,代码如下:

class Student : Person
{
private string strClass; private string strAddress; public void Address(string cla, string adre)
{
//这里的this关键字调用了子类的成员和父类的非似有成员
this.strClass = "五";
this.strAddress = "北京";
this.strName = "子强"; //这里base关键字调用了是父类的非似有成员
base.strName = "强子"; Console.WriteLine("我是{0}年纪,来自{1}", cla, adre);
}
public void Sing()
{
this.strClass = "";
Console.WriteLine("我可以唱歌!");
}
}

所以在子类中this关键字和base关键字的访问范围的示意图如下:

二,关于子类对象的构造函数和父类构造函数的执行顺序

我们分别为父类和子类添加显式的构造函数,代码如下

class Person
{
private int nAge;
protected string strName;
double douHeight;
public string strEateType; //父类的构造函数
public Person()
{
Console.WriteLine("我是父类的构造函数");
} public void Hello()
{
Console.WriteLine("我可以说Hello!");
}
public void Run()
{
Console.WriteLine("我可以跑!");
} } class Student : Person
{
private string strClass; private string strAddress; //子类的构造函数
public Student ()
{
Console.WriteLine("我是子类的构造函数");
}
}

我们使用VS的单步调试,来看父类和子类显式构造函数的执行顺序,如下图(动态图片,可以看到过程):

很容易的可以发现,当创建子类对象的时候

①先调用了子类的构造函数

②调用了父类的构造函数

③执行了父类的构造函数

④执行了子类的构造函数

那么为什么会这样呢?

我尝试通过反编译看源码来解释这个原因,但是反编译的结果如下,

没有发现有什么特别的地方可以解释这个原因。

最后还是查阅微软的MSDN官方文档找到了答案(原文地址点击这里

根据微软官方的代码示例,那么下面的代码的效果也是相同的

//子类的构造函数
public Student ()
{
Console.WriteLine("我是子类的构造函数"); } //这里的代码和上面的代码效果是相同的
public Student()
:base()
{
Console.WriteLine("我是子类的构造函数");
}

也就是说只要在子类显式的声明了无参的构造函数,在实例化子类的对象是,子类的无参构造函数都会去调用父类无参的构造函数。

那么,如果父类没有这个无参的构造函数则会报错。

如下面的代码:

 class Person
{
private int nAge;
protected string strName;
double douHeight;
public string strEateType; //父类的构造函数
//public Person()
//{
// Console.WriteLine("我是父类的构造函数");
//}       //父类的有参数的构造函数,这里覆盖了无参的构造函数
public Person (string str)
{
Console.WriteLine("我是父类的构造函数{0}",str);
} public void Hello()
{
Console.WriteLine("我可以说Hello!");
}
public void Run()
{
Console.WriteLine("我可以跑!");
}
} class Student : Person
{
private string strClass; private string strAddress; //子类的无参构造函数
public Student ()
{
Console.WriteLine("我是子类的构造函数"); } public Student(string strName)
{
Console.WriteLine("我的名字叫{0}",strName);
}
}

这时候编译会报错,

因为在父类中有参数的构造函数覆盖了无参数的构造函数,所以在子类的无参数的构造函数没办法回调父类的无参数的构造函数初始化父类的成员变量。所以报错。

那么在初始化子类的时候,为什么要调用父类的构造函数呢?

在初始化子类之前需要通过构造函数初始化父类的成员变量

父类的构造函数先于子类的构造函数执行的意义是什么呢?

当在父类的构造函数中和子类的构造函数中为父类的非私有成员变量赋不同默认值。当实例化子类,子类要调用构造函数初始化成员变量,如果先执行了子类的构造函数,再执行父类的构造函数,父类成员字段的值会覆盖子类成员字段的值。但是我们想得到的是子类的属性值。所以为了解决数据冲突,父类的构造函数要先于子类的构造函数执行。

如下面的代码:

class Person
{
private int nAge;
private string strName;
double douHeight;
public string strEateType; // 父类的构造函数
public Person()
{
//再父类中对strEateType赋初始值
this.strEateType = "吃饭";
Console.WriteLine("我是父类的构造函数{0}", strEateType);
}
} class Student : Person
{
private string strClass;
private string strAddress; //子类的构造函数
public Student()
{
//在子类中对strEateType赋初始值
this.strEateType = "吃面条";
Console.WriteLine("我是子类的构造函数{0}",strEateType); }
}

这时候我们通过,声明子类对象访问strEateType的值,如下:

Student stu1 = new Student();
//stu1.
string str = stu1.strEateType.ToString();
Console.WriteLine(str); Console.ReadKey();

这里肯定是要打印出子类的属性strEateType的值,如果先执行子类构造函数对strEateType赋值,然后父类的构造函数赋值覆盖strEateType的初始值。那么打印出的将是父类成员字段的值。所以,父类的构造函数先于子类的构造函数执行。

打印结果如下:

三,子类是否可以有和父类的同名方法

看下面的代码,我们声明一个父类Person:

 class Person
{
private int nAge;
private string strName;
double douHeight;
public string strEateType; public readonly string strrrr;
// 父类的构造函数
public Person()
{
this.strEateType = "吃饭";
Console.WriteLine("我是父类的构造函数{0}", strEateType);
} public Person(string str)
{
this.strName = str;
Console.WriteLine("我是父类的构造函数{0}", str);
} public void Hello()
{
Console.WriteLine("我可以说地球人的Hello!");
}
public void Run()
{
Console.WriteLine("我可以跑!");
}
}

声明一个子类继承Person,代码如下:

class Worker:Person
{
public void Hello()
{
Console.WriteLine("我是工人会说Hello!");
} public new void Run()
{
Console.WriteLine("我是工人我会奔跑!");
}
}

然后实例化Worker对象,打印Hello方法,结果如下图:

 

这是为什么呢?编译器已经告诉了我们,如下图:


看出来是子类的方法隐藏了父类的方法。

既然子类可以定义和父类同名的方法,那么是否可以定同名的字段呢?答案是肯定的,而且会像同名方法一样,子类同名字段会隐藏父类同名的字段。

【C#小知识】C#中一些易混淆概念总结(五)---------深入解析C#继承的更多相关文章

  1. 【C#小知识】C#中一些易混淆概念总结(七)---------解析抽象类,抽象方法

    目录: [C#小知识]C#中一些易混淆概念总结--------数据类型存储位置,方法调用,out和ref参数的使用 [C#小知识]C#中一些易混淆概念总结(二)--------构造函数,this关键字 ...

  2. 【C#小知识】C#中一些易混淆概念总结(六)---------解析里氏替换原则,虚方法 分类: C# 2014-02-08 01:53 1826人阅读 评论(0) 收藏

    目录: [C#小知识]C#中一些易混淆概念总结--------数据类型存储位置,方法调用,out和ref参数的使用 [C#小知识]C#中一些易混淆概念总结(二)--------构造函数,this关键字 ...

  3. 【C#小知识】C#中一些易混淆概念总结(五)---------继承 分类: C# 2014-02-06 22:05 1106人阅读 评论(0) 收藏

    目录: [C#小知识]C#中一些易混淆概念总结--------数据类型存储位置,方法调用,out和ref参数的使用 [C#小知识]C#中一些易混淆概念总结(二)--------构造函数,this关键字 ...

  4. 【C#小知识】C#中一些易混淆概念总结(四)---------解析Console.WriteLine() 分类: C# 2014-02-05 17:18 1060人阅读 评论(0) 收藏

    目录: [C#小知识]C#中一些易混淆概念总结 [C#小知识]C#中一些易混淆概念总结(二) [C#小知识]C#中一些易混淆概念总结(三) ------------------------------ ...

  5. 【C#小知识】C#中一些易混淆概念总结(三)---------结构,GC,静态成员,静态类

    目录: [C#小知识]C#中一些易混淆概念总结 [C#小知识]C#中一些易混淆概念总结(二) ---------------------------------------分割线----------- ...

  6. 【C#小知识】C#中一些易混淆概念总结(二)--------构造函数,this关键字,部分类,枚举 分类: C# 2014-02-03 01:24 1576人阅读 评论(0) 收藏

    目录: [C#小知识]C#中一些易混淆概念总结--------数据类型存储位置,方法调用,out和ref参数的使用 继上篇对一些C#概念问题进行细节的剖析以后,收获颇多.以前,读书的时候,一句话一掠而 ...

  7. C#中一些易混淆概念总结

    C#中一些易混淆概念 这几天一直在复习C#基础知识,过程中也发现了自己以前理解不清楚和混淆的概念.现在给大家分享出来我的笔记: 一,.NET平台的重要组成部分都是有哪些 1)FCL (所谓的.NET框 ...

  8. 【C#小知识】C#中一些易混淆概念总结---------数据类型存储,方法调用,out和ref参数的使用

    这几天一直在复习C#基础知识,过程中也发现了自己以前理解不清楚和混淆的概念.现在给大家分享出来我的笔记: 一,.NET平台的重要组成部分都是有哪些 1)FCL (所谓的.NET框架类库) 这些类是微软 ...

  9. 【C#小知识】C#中一些易混淆概念总结(八)---------解析接口 分类: C# 2014-02-18 00:09 2336人阅读 评论(4) 收藏

     这一篇主要来解析关于面向对象中最总要的一个概念--接口. 对于接口来说,C#是有规定使用Interface关键字来声明接口.它的声明是和类一致的.可以说接口就是一个特殊的抽象类.如下代码: cl ...

随机推荐

  1. python编码(六)

    1. 字符编码简介 1.1. ASCII ASCII(American Standard Code for Information Interchange),是一种单字节的编码.计算机世界里一开始只有 ...

  2. Linux常见配置文件

    一./etc 配置文件 /etc/passwd 用户数据库,其中的域给出了用户名.真实姓名.家目录.加密口令和用户的其他信息 /etc/group 类似/etc/passwd ,但说明的不是用户而是组 ...

  3. Android绘图板的开发

    >>继承自View >>使用Canvas绘图 每次View组件上的图形状态数据发生了改变,都应该通知View组件重写调用onDraw(Canvas canvas)方法重绘该组件 ...

  4. (并查集 添加关系)How Many Answers Are Wrong --Hdu --3038

    链接: http://acm.hdu.edu.cn/showproblem.php?pid=3038 http://acm.hust.edu.cn/vjudge/contest/view.action ...

  5. (能被11整除的数的特征)The shortest problem --hdu

    链接: http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1005&cid=595 若一个整数的个位数字截去,再从余下的数中 ...

  6. 洛谷P3567[POI2014]KUR-Couriers(主席树+二分)

    题意:给一个数列,每次询问一个区间内有没有一个数出现次数超过一半 题解: 最近比赛太多,都没时间切水题了,刚好日推了道主席树裸题,就写了一下 然后 WA80 WA80 WA0 WA90 WA80 ?? ...

  7. spring MVC controller中的方法跳转到另外controller中的某个method的方法

    1. 需求背景     需求:spring MVC框架controller间跳转,需重定向.有几种情况:不带参数跳转,带参数拼接url形式跳转,带参数不拼接参数跳转,页面也能显示. 本来以为挺简单的一 ...

  8. java多线程 —— 两种实际应用场景模拟

    最近做的偏向并发了,因为以后消息会众多,所以,jms等多个线程操作数据的时候,对共享变量,这些要很注意,以防止发生线程不安全的情况. (一) 先说说第一个,模拟对信息的发送和接收.场景是这样的: 就像 ...

  9. 挂起的更改中的“解析”是什么意思?原来是微软错误的翻译

    [2017.4.5 补充] 收到微软TFS产品组的回复,由于版本分支丢失了本来已经修复的内容,并确认下一个版本将修复这个问题. 自从团队资源管理器的"挂起的更改中"可以链接相关工作 ...

  10. 【NumberValidators】增值税发票代码验证

    同大陆身份证验证一样,该部分是按照国家增值税发票代码的定制规则,进行发票代码验证,如果需要查验发票信息是否正确,应该通过第三方接口(大约一毛钱查验一次),或者直接上国家税务总局全国增值税发票查验平台进 ...