C#类继承中构造函数的执行序列示例详解
作者:yixuan.han 发布时间:2022-05-26 11:42:09
前言
大家都知道类的继承规则:
1、派生类自动包含基类的所有成员。但对于基类的私有成员,派生类虽然继承了,但是不能在派生类中访问。
2、所有的类都是按照继承链从顶层基类开始向下顺序构造。最顶层的基类是System.Object
类,所有的类都隐式派生于它。只要记住这条规则,就能理解派生类在实例化时对构造函数的调用过程。
不知道大家在使用继承的过程中有木有遇到过调用构造函数时没有按照我们预期的那样执行呢?一般情况下,出现这样的问题往往是因为类继承结构中的某个基类没有被正确实例化,或者没有正确给基类构造函数提供信息,如果理解在对象生命周期的这个阶段发生的事情,将更利于解决此类问题。
为了实例化派生的类,必须先实例化它的基类。而要实例化这个基类。又必须要实例化这个基类的基类,这样一直到实例化System.Object
(所有类的跟)为止,结果无论使用什么构造函数实例化一个类,总是首先调用System.Object.Object()
.
下面一个示例演示执行顺序:
基类:
public class MyBaseClass
{
public MyBaseClass()
{
Console.WriteLine("I am MyBaseClass()");
}
public MyBaseClass(int i)
{
Console.WriteLine("I am MyBaseClass(int i)");
}
}
派生类:
public MyDerivedClass()
{
Console.WriteLine("I am MyDerivedCalss()");
}
public MyDerivedClass(int i)
{
Console.WriteLine("I am MyDerivedClass(int i)");
}
public MyDerivedClass(int i,int j)
{
Console.WriteLine("I am MyDerivedClass(int i,int j)");
}
接下来我们在Main函数中以不带参数的构造函数实例化MyDerivedClass:
MyDerivedClass myObj = new MyDerivedClass();
运行程序,控制台输出如下:
从结果可以看出,执行顺序先是基类构造的函数,接下来才是派生类的构造函数,即
1.执行System.Object.Object()
构造函数(Object比较特殊,所有类的基类,一般可以不考虑,但是得知道它也是被执行了的)
2.执行MyBaseClass.MyBaseClass()
构造函数
3.执行MyDerivedClass.MyDerivedClass()
构造函数
如果我们以带一个参数的构造函数实例化MyDerivedClass:
MyDerivedClass myObj = new MyDerivedClass(4);
运行程序,控制台输出如下:
可以看出执行顺序如下:
1.执行System.Object.Object()
构造函数
2.执行MyBaseClass.MyBaseClass()
构造函数
3.执行MyDerivedClass.MyDerivedClass(int i)
构造函数
同理如果我们以带两个参数的构造函数实例化MyDerivedClass
MyDerivedClass myObj = new MyDerivedClass(4,8);
运行程序,控制台输出如下:
可以看出执行顺序如下:
1.执行System.Object.Object()
构造函数
2.执行MyBaseClass.MyBaseClass()
构造函数
3.执行MyDerivedClass.MyDerivedClass(int i,int j)
构造函数
大多数情况下这个都能正常工作,但是有时我们需要对发生的事件进行更多的控制。比如我们想得到如下所示的执行顺序:
1.执行System.Object.Object()构造函数
2.执行MyBaseClass.MyBaseClass(int i)
构造函数
3.执行MyDerivedClass.MyDerivedClass(int i,int j)
构造函数
使用这个顺序,可以把使用int i参数的代码放到MyBaseClass(int i)
中,MyDerivedClass(int i,int j)
只需要处理int j(假设int i参数在MyBaseClass和 MyDerivedClass里含义是一样的)
为此,只需要使用构造函数初始化器,把代码放到方法定义的冒号后面,如在派生类的构造函数中指定所使用的基类的构造函数,如下所示:
public MyDerivedClass(int i,int j) : base(i)
{
Console.WriteLine("I am MyDerivedClass(int i,int j)");
}
其中,base关键字指定在实例化过程中使用具有指定参数的构造函数。这里使用了int参数,其值通过i传递给MyDerivedClass构造函数,所以将使用MyBaseClass(int i)
,这样就不会调用MyBaseClass()
了,我们重新执行下前面两个参数的实例化代码,就可以看出执行结果确实如此:
除了base关键字,还可以使用this关键字用作构造函数初始化器,这个关键字指定在调用指定的构造函数前,实例化过程对当前类使用非默认的构造函数。例如:
public MyDerivedClass():this(5,6)
{
Console.WriteLine("I am MyDerivedCalss()");
}
使用MyDerivedCalss()
构造函数实例化,执行顺序是:
1.执行System.Object.Object()
构造函数
2.执行MyBaseClass.MyBaseClass(int i)
构造函数
3.执行MyDerivedClass.MyDerivedClass(int i,int j)
构造函数
4.执行MyDerivedClass.MyDerivedClass()
构造函数
总之呢,无论派生类上使用什么样的构造函数(默认的or不是默认的),除非明确指定(如使用base关键字),否则就先调用用基类的默认构造函数。
来源:https://www.cnblogs.com/yixuanhan/p/9596454.html


猜你喜欢
- 前言最近在做一个公共相关的内容,公告里边的内容,打算做成配置化的。但是考虑到存储到数据库,需要建立数据库表;存储到配置组件中,担心配置组件存
- 在一个完整的项目中,如果每一个控制器的方法都返回不同的结果,那么对项目的维护和扩展都会很麻烦;并且现在主流的开发模式时前后端分离的模式,如果
- @Profiles和@PropertySource根据环境切换配置文件使用@PropertySource注解加载配置文件,并制定解析配置文件
- 引言我已经一个多星期没碰过电脑了,今日上班,打开电脑的第一件事就是想着写点什么。反正大家都还沉浸在节后的喜悦中,还没进入工作状态,与其浪费时
- 一、准备工作1、确定电脑上已经成功安装jdk7.0以上版本2、win10操作系统3、maven安装包 下载地址:http://maven.a
- 前言项目中检测人脸图片是否合法的功能,之前用的是百度的人脸识别接口,由于成本高昂不得不寻求替代方案。什么是opencv?OpenCV是一个基
- java 代码块与静态代码块加载顺序public abstract class ClassLoadingTest {public stati
- 枚举是迭代一个集合中的数据项的过程。我们经常使用的大多数集合实际上都已经实现了枚举的接口IEnumerable和IEnumerator接口,
- public partial class MonthCalendarForm : Form{ public MonthCalen
- RabbitMQ主要有六种种工作模式,本文整合SpringBoot分别介绍工作模式的实现。前提概念生产者消息生产者或者发送者,使用P表示:队
- 本文实例讲述了C#实现对二维数组排序的方法。分享给大家供大家参考。具体实现方法如下:/// <summary>/// A gen
- 一、背景知识:树(Tree)在之前的笔记中,我们介绍的链表、栈、队列、数组和字符串都是以线性结构来组织数据的。本篇笔记要介绍的树采用的是树状
- 本文实例为大家分享了java实现猜拳游戏的具体代码,供大家参考,具体内容如下package com.farsight.session7;im
- 今天可是遇到一个很简单的需求,但是却让我蛋疼了半天。滑动屏幕控制物体旋转,但是旋转的角度要在-60到60之间。乍一听这简直是小儿科啊。判断一
- windows xp下配置JDK环境变量:1.安装JDK,安装过程中可以自定义安装目录等信息,例如我们选择安装目录为D:/java/jdk1
- 因为mybatis好使,所以几乎需要操作数据库的时候,我都会使用mybatis,而且在一个正式的项目中,同时存在BS和CS的程序,都使用的M
- MQ:消息队列/消息中间件/消息代理,产品有很多,ActiveMQ RabbitMQ RocketMQ Kafka1、Stream解决的痛点
- 请求路径匹配路由在spring中,当一个请求过来的时候会做路径匹配,下面我们就从源码层面分析一下路径匹配。示例:@RequestMappin
- 前言在项目开发中经常会用到配置文件,配置文件的存在解决了很大一份重复的工作。今天就分享四种在Springboot中获取配置文件的方式。注:前
- 前言:java5为我们提供了Callable和Future,使我们可以很容易的完成异步任务结果的获取,但是通过Future的get获取异步任