不知道大家在使用继承的过程中有木有遇到过调用构造函数时没有按照我们预期的那样执行呢?一般情况下,出现这样的问题往往是因为类继承结构中的某个基类没有被正确实例化,或者没有正确给基类构造函数提供信息,如果理解在对象生命周期的这个阶段发生的事情,将更利于解决此类问题。

为了实例化派生的类,必须先实例化它的基类。而要实例化这个基类。又必须要实例化这个基类的基类,这样一直到实例化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();

运行程序,控制台输出如下:

可以看出执行顺序如下:

1.执行System.Object.Object()构造函数

2.执行MyBaseClass.MyBaseClass()构造函数

3.执行MyDerivedClass.MyDerivedClass(int i)构造函数

同理如果我们以带两个参数的构造函数实例化MyDerivedClass

  MyDerivedClass myObj = new MyDerivedClass(,);

运行程序,控制台输出如下:

可以看出执行顺序如下:

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(,)
{
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关键字),否则就先调用用基类的默认构造函数。

C#类继承中构造函数的执行序列的更多相关文章

  1. C++类继承中的构造函数和析构函数 调用顺序

    思想: 在C++的类继承中,构造函数不能被继承(C11中可以被继承,但仅仅是写起来方便,不是真正的继承) 建立对象时,首先调用基类的构造函数,然后在调用下一个派生类的构造函数,依次类推: 析构对象时, ...

  2. (C++)C++类继承中的构造函数和析构函数

    思想: 在C++的类继承中, 建立对象时,首先调用基类的构造函数,然后在调用下一个派生类的构造函数,依次类推: 析构对象时,其顺序正好与构造相反: 例子: #include <iostream& ...

  3. C# 类继承中的私有字段都去了哪里?

    最近在看 C++ 类继承中的字段内存布局,我就很好奇 C# 中的继承链那些 private 字段都哪里去了? 在内存中是如何布局的,毕竟在子类中是无法访问的. 一:举例说明 为了方便讲述,先上一个例子 ...

  4. C++ 类的继承五(类继承中的static关键字)

    //类继承中的static关键字 #include<iostream> using namespace std; /* 派生类中的静态成员 基类定义的静态成员,将被所有派生类共享 根据静态 ...

  5. C++ 类的继承四(类继承中的重名成员)

    //类继承中的重名成员 #include<iostream> using namespace std; /* 自己猜想: 对于子类中的与父类重名的成员,c++编译器会单独为子类的这个成员变 ...

  6. C++类的继承中构造函数和析构函数调用顺序例子

    /*当建立一个对象时,首先调用基类的构造函数,然后调用下一个派生类的构造函数,依次类推,直至到达派生类次数最多的派生次数最多的类的构造函数为止.简而言之,对象是由“底层向上”开始构造的.因为,构造函数 ...

  7. C++类继承中,基类/当前对象属性/当前对象的构造顺序

    [1]中提到,规范的派生类构造函数三个要点: 首先创建基类对象 应通过成员初始化列表,创建基类对象 应该初始化本派生类新增的成员变量 那在构造派生类实例的过程中,其基类(以及多继承的时候多个基类)/当 ...

  8. C# 类型运算符重载在类继承中的调用测试

    这是一篇晦涩难懂的片面的研究 一,简单的继承层次 class CA { } class CB : CA{ } class CC : CB{ } } void Test(CA oa){//CATest ...

  9. C++基础——类继承中方法重载

    一.前言 在上一篇C++基础博文中讨论了C++最基本的代码重用特性——类继承,派生类可以在继承基类元素的同时,添加新的成员和方法.但是没有考虑一种情况:派生类继承下来的方法的实现细节并不一定适合派生类 ...

随机推荐

  1. Android 开发学习资源汇总

    下面这些资源对Android开发来说是很有帮助的! 1. 在线代码运行工具 地址:https://tool.lu/coderunner/ 说明:此工具站能在线运行C.C++.Java,基本能满足相关基 ...

  2. 0. VIM 系列 - 源码升级最新版本vim

    卸载原来的vim: $ sudo apt-get remove --purge vim $ suso apt-get clean 下载最新版本源码: $ git clone https://githu ...

  3. AI - TensorFlow - 过拟合(Overfitting)

    过拟合 过拟合(overfitting,过度学习,过度拟合): 过度准确地拟合了历史数据(精确的区分了所有的训练数据),而对新数据适应性较差,预测时会有很大误差. 过拟合是机器学习中常见的问题,解决方 ...

  4. python进程和线程(四)

    线程同步条件.信号量及队列 同步条件(event) 下面是官方文档对event的一些解释: An event is a simple synchronization object; the event ...

  5. java~gradle构建公用包并上传到仓库

    java~gradle构建公用包并上传到仓库 我们一般会把公用的代码放在一个包里,然后其它 项目可以直接使用,就像你使用第三方包一样! 仓库 存储包的地方叫做仓库,一般可以分为本地仓库和远程仓库,本地 ...

  6. [macOS开发.NET Core] 一个简单的WEB程序

    上一篇咱们提到了在macOS下选进行开发. 咱们已经把工具准备完成了. 现在咱们做一个简单的DEMO 创建WEB程序  之前咱们已经创建过WEB程序,并且成功的运行过数据 现在咱们创建一个页面,并显 ...

  7. Python实战171203统计

    统计序列中元素出现的频次 如何统计出某一个随机数列的元素出现的次数是多少? import randomdata=[random.randint(0,7) for _ in range(15)]c=di ...

  8. [Linux] deepin与nginx

    deepin Linux Deepin 是一个基于 DEB 包管理的一个独立操作系统,和那些 Ubuntu(下个大版本是基于debian开发) 的衍生版仅仅只是换主题.调整ISO预置的软件包不同.Li ...

  9. java之servlet入门操作教程一

    这篇文章主要用来记录,进行servlet开发的一些简单操作步骤,帮助直接上手操作 准备: java环境配置:已配置(javac检验) myeclipse开发环境:已安装 tomcat服务器:已安装(或 ...

  10. 初步认识Swiper_前端交互控制神器_滚动3D切换等特效简单制作

    前言: 本人在项目的工作中负责研发,页面及交互基本都是交给前端去做的.以前前端写的东西大概都知道,都是一些JS,CSS和HTML等的一些基本控制,都懂!但是今天前端突然做了一个具有特殊效果的DOM:页 ...