目录:

【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方法,结果如下图:

 

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



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

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


毕业实习交流群:221376964。你也可以关注我的新浪微博进行交流。

版权声明:本文为博主原创文章,未经博主允许不得转载。

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

  1. VS2010中使用命令行参数 分类: c/c++ 2014-07-11 22:24 634人阅读 评论(0) 收藏

    在Linux下编程习惯了使用命令行参数,故使用VS2010时也尝试了一下. 新建项目,c++编写程序如下: #include<iostream> #include<fstream&g ...

  2. jdbc知识问答 分类: 面试 2015-07-10 22:05 5人阅读 评论(0) 收藏

    1 JDBC连接数据库6步 Load the JDBC Driver Establish the Database Connection Create a Statement Object Execu ...

  3. iOS中UITextField 使用全面解析 分类: ios技术 2015-04-10 14:37 153人阅读 评论(0) 收藏

    //初始化textfield并设置位置及大小   UITextField *text = [[UITextField alloc]initWithFrame:CGRectMake(20, 20, 13 ...

  4. 解决ORA-29857:表空间中存在域索引和/或次级对象 & ORA-01940:无法删除当前连接的用户问题 分类: oracle sde 2015-07-30 20:13 8人阅读 评论(0) 收藏

    今天ArcGIS的SDE发生了一点小故障,导致系统表丢失,所以需要重建一下SDE数据库,在删除SDE用户和所在的表空间过程中遇到下面两个ORA错误,解决方法如下: 1)删除表空间时报错:ORA-298 ...

  5. centos中的配置文件 分类: B3_LINUX 2015-04-03 22:21 184人阅读 评论(0) 收藏

    /etc/profile:此文件为系统的每个用户设置环境信息,当用户第一次登录时,该文件被执行.并从/etc/profile.d目录的配置文件中搜集shell的设置. /etc/bashrc:为每一个 ...

  6. C/C++中const的用法 分类: C/C++ 2015-07-05 00:43 85人阅读 评论(0) 收藏

    const是C语言的关键字,经C++进行扩充,变得功能强大,用法复杂.const用于定义一个常变量(只读变量),当const与指针,引用,函数等结合起来使用时,情况会变得复杂的多.下面将从五个方面总结 ...

  7. 认识C++中的临时对象temporary object 分类: C/C++ 2015-05-11 23:20 137人阅读 评论(0) 收藏

    C++中临时对象又称无名对象.临时对象主要出现在如下场景. 1.建立一个没有命名的非堆(non-heap)对象,也就是无名对象时,会产生临时对象. Integer inte= Integer(5); ...

  8. Java获取项目中的路径 分类: Java Game 2014-08-14 10:17 122人阅读 评论(0) 收藏

    在项目中经常需要获取某个文件的路径: 在这里提供一些获取路径的方法.. 1.此种方式获取的路径,是当前类所在的路径: UserDAOTest.class.getResource("UserD ...

  9. Struts知识问答 分类: 面试 2015-07-10 22:01 4人阅读 评论(0) 收藏

    1. 简述Struts框架的初始化流程. 答案: 对于采用Struts框架的Web应用,在Web应用启动时就会加载并初始化控制器ActionServlet ActionServlet从struts-c ...

随机推荐

  1. Python学习-37.Python中的正则表达式

    作为一门现代语言,正则表达式是必不可缺的,在Python中,正则表达式位于re模块. import re 这里不说正则表达式怎样去匹配,例如\d代表数字,^代表开头(也代表非,例如^a-z则不匹配任何 ...

  2. Grafana安装及配置

    安装 sudo yum install https://grafanarel.s3.amazonaws.com/builds/grafana-2.1.3-1.x86_64.rpm 启动 sudo se ...

  3. Microsoft SQL Server 2012 管理 (1): 安装配置SQL Server 重点

    SQL Server 可以在实例,数据库,列,查询分别指定排序规则 /* Module 1 - working with Clollations */ -- 1.1 Obtain the Instan ...

  4. sun.jersey使用Jackson转换数据

    差点被com.sun.jersey自身的json转换吓死,遇到List等类型,会把这些也转换为json对象,而不是jsonarray. 被园里的同行拯救了,在web.xml中配置一下就ok. < ...

  5. gdb用法

    mickole@test:~/ctest/05gdb$ gdb simple //开始gdb调试 GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4- ...

  6. 安装CentOS桌面环境

    CentOS 作为服务器的操作系统是很常见的,但是因为需要稳定而没有很时髦的更新,所以很少做为桌面环境.在服务器上通常不需要安装桌面环境,最小化地安装 CentOS(也就是 minimal CentO ...

  7. artdialog(4.1.7)弹出框

    artDialog弹出框 <link href="/js/artDialog/skins/default.css" rel="stylesheet" /& ...

  8. BitAdminCore框架更新日志20180523

    20180523更新内容 本次更新两个内容,一是增加视频处理功能,二是增加定时服务功能. 视频处理 定时服务 BitAdminCore框架,用最少的代码,实现最多的功能 本次新暂未发布,后续有空发布 ...

  9. Algebraic Kernel ( Arithmetic and Algebra) CGAL 4.13 -User Manual

    1 Introduction Real solving of polynomials is a fundamental problem with a wide application range. T ...

  10. MYsql 之单标查询.

    http://www.cnblogs.com/wangfengming/articles/8064956.html .数据操作 .增 INSERT into t2(name,age) VALUES() ...