// 父类
class People
{public People(){Console.WriteLine("执行People构造函数!");}public virtual void Say(){Console.WriteLine("People Hello");}~People(){Console.WriteLine("执行People析构函数!");}
}
// 子类
class Student:People
{public Student(){Console.WriteLine("执行Student构造函数!");}public override void Say(){Console.WriteLine("Student Hello");}~Student(){Console.WriteLine("执行Student析构函数!");}
}
// Program.cs
using System;namespace ConsoleAppTest
{class Program{static void Main(string[] args){People people = new Student();people.Say();Student student = new Student();student.Say();}}
}

输出顺序为:
执行People构造函数!(先执行父类的构造函数)
执行Student构造函数!(后执行子类的构造函数)
Student Hello
执行People构造函数!
执行Student构造函数!
Student Hello
执行Student析构函数!(先执行子类的析构函数)
执行People析构函数!(后执行父类的析构函数)
执行Student析构函数!
执行People析构函数!

父类用virtual声明的方法为虚方法,子类要重写父类的虚方法,需要使用override关键字声明为重写方法。否则,父类方法用virtual声明,而子类方法不用override声明,就不是方法重写,导致main函数中父类对象调用父类方法,子类对象调用子类方法。或者,子类方法用override声明,而父类方法不用virtual声明,则编译直接报错。如果子类方法用override声明,父类方法用abstract声明,则需要父类也用abstract声明,这时父类是抽象类,不能实例化。如果父类不用virtual声明,子类也不用override声明,而是两个相同名字,相同参数的方法,这时不是重写,main函数调用时,父类对象调用父类方法,子类对象调用子类方法,如下:

// 父类
class People
{public People(){Console.WriteLine("执行People构造函数!");}public void Say(){Console.WriteLine("People Hello");}~People(){Console.WriteLine("执行People析构函数!");}
}
// 子类
class Student:People
{public Student(){Console.WriteLine("执行Student构造函数!");}public void Say(){Console.WriteLine("Student Hello");}~Student(){Console.WriteLine("执行Student析构函数!");}
}
// Program.cs
using System;namespace ConsoleAppTest
{class Program{static void Main(string[] args){People people = new Student();people.Say();Student student = new Student();student.Say();}}
}

输出顺序为:
执行People构造函数!
执行Student构造函数!
People Hello(父类对象调用父类方法)
执行People构造函数!
执行Student构造函数!
Student Hello(子类对象调用子类方法)
执行Student析构函数!
执行People析构函数!
执行Student析构函数!
执行People析构函数!

如果子类的重写方法里需要调用父类的同名方法,则如下写法:

// 父类
class People
{public People(){Console.WriteLine("执行People构造函数!");}public virtual void Say(){Console.WriteLine("People Hello");}~People(){Console.WriteLine("执行People析构函数!");}
}
// 子类
class Student:People
{public Student(){Console.WriteLine("执行Student构造函数!");}public override void Say(){base.Say();// int id = base.id;Console.WriteLine("Student Hello");}~Student(){Console.WriteLine("执行Student析构函数!");}
}
// Program.cs
using System;namespace ConsoleAppTest
{class Program{static void Main(string[] args){People people = new Student();people.Say();Student student = new Student();student.Say();}}
}

输出顺序为:
执行People构造函数!
执行Student构造函数!
People Hello(调用父类的重写方法)
Student Hello
执行People构造函数!
执行Student构造函数!
People Hello
Student Hello
执行Student析构函数!
执行People析构函数!
执行Student析构函数!
执行People析构函数!

将父类的virtual关键字和子类的override关键字去掉,则输出顺序为:
执行People构造函数!
执行Student构造函数!
People Hello(父类对象调用父类方法)
执行People构造函数!
执行Student构造函数!
People Hello(子类对象调用子类方法,子类方法中用base调用父类方法)
Student Hello
执行Student析构函数!
执行People析构函数!
执行Student析构函数!
执行People析构函数!

因为父类对象调用的是父类的Say()方法,没有执行子类的Say()方法。
执行子类的无参构造函数时,先调用父类的无参构造函数。
如果子类有有参构造函数,则执行子类的有参构造函数也默认先调用父类的无参构造函数,即使父类有有参构造函数也不会调用,如下所示。

// 父类
class People
{public People(){Console.WriteLine("执行People的无参构造函数!");}public People(string name){Name = name;Console.WriteLine(name + ":执行People的有参构造函数!");}public virtual void Say(){Console.WriteLine("People Hello");}~People(){Console.WriteLine("执行People析构函数!");}
}
// 子类
class Student:People
{public Student(){Console.WriteLine("执行Student的无参构造函数!");}public Student(string name){Name = name;Console.WriteLine(name + ":执行Student的有参构造函数!");}public override void Say(){Console.WriteLine("Student Hello");}~Student(){Console.WriteLine("执行Student析构函数!");}
}
// Program.cs
using System;namespace ConsoleAppTest
{class Program{static void Main(string[] args){People people = new Student("Alice");people.Say();Student student = new Student("Bob");student.Say();}}
}

输出顺序如下:
执行People的无参构造函数!
Alice:执行Student的有参构造函数!
Student Hello
执行People的无参构造函数!
Bob:执行Student的有参构造函数!
Student Hello
执行Student析构函数!
执行People析构函数!
执行Student析构函数!
执行People析构函数!

子类的有参构造函数调用父类的有参构造函数:

// 父类
class People
{public People(){Console.WriteLine("执行People的无参构造函数!");}public People(string name){Name = name;Console.WriteLine(name + ":执行People的有参构造函数!");}public virtual void Say(){Console.WriteLine("People Hello");}~People(){Console.WriteLine(Name + ":执行People析构函数!");}
}
// 子类
class Student:People
{public Student(){Console.WriteLine("执行Student的无参构造函数!");}public Student(string name):base("Jack"){Name = name;Console.WriteLine(name + ":执行Student的有参构造函数!");}public override void Say(){Console.WriteLine("Student Hello");}~Student(){Console.WriteLine(Name + ":执行Student析构函数!");}
}
// Program.cs
using System;namespace ConsoleAppTest
{class Program{static void Main(string[] args){People people = new Student("Alice");people.Say();Student student = new Student("Bob");student.Say();}}
}

输出顺序如下:
Jack:执行People的有参构造函数!(调用父类的有参构造函数)
Alice:执行Student的有参构造函数!
Student Hello
Jack:执行People的有参构造函数!
Bob:执行Student的有参构造函数!
Student Hello
Bob:执行Student析构函数!(后构造的对象先析构)
Jack:执行People析构函数!
Alice:执行Student析构函数!
Jack:执行People析构函数!

抽象类与子类,抽象方法与具体方法:

// 抽象类
abstract class Animal
{public string Name { get; set; }public Animal(){Console.WriteLine("执行抽象类的无参构造函数!");}public Animal(string name){Name = name;Console.WriteLine(name + "执行抽象类的有参构造函数!");}public abstract void eat();~Animal(){Console.WriteLine(Name + "执行抽象类的析构函数!");}
}
// 子类
class Cat : Animal
{public string Name { get; set; }public Cat(){Console.WriteLine("执行子类的无参构造函数!");}public Cat(string name):base("BigCat"){Name = name;Console.WriteLine(name + "执行子类的有参构造函数!");}public override void eat(){Console.WriteLine(Name + "执行子类的eat()方法!");}~Cat(){Console.WriteLine(Name + "执行子类的析构函数!");}
}
// Program.cs
using System;namespace ConsoleAppTest
{class Program{static void Main(string[] args){Animal animal = new Cat();animal.eat();Animal animal1 = new Cat("SmallCat");animal1.eat();}}
}

输出顺序如下:
执行抽象类的无参构造函数!
执行子类的无参构造函数!
执行子类的eat()方法!
BigCat执行抽象类的有参构造函数!
SmallCat执行子类的有参构造函数!
SmallCat执行子类的eat()方法!
SmallCat执行子类的析构函数!
BigCat执行抽象类的析构函数!
执行子类的析构函数!
执行抽象类的析构函数!

一文读懂C#中的抽象类、抽象方法、virtual虚函数、override重写函数及父类子类构造函数和析构函数的执行顺序相关推荐

  1. java中date类型如何赋值_一文读懂java中的Reference和引用类型

    简介 java中有值类型也有引用类型,引用类型一般是针对于java中对象来说的,今天介绍一下java中的引用类型.java为引用类型专门定义了一个类叫做Reference.Reference是跟jav ...

  2. 一文读懂Java中File类、字节流、字符流、转换流

    一文读懂Java中File类.字节流.字符流.转换流 第一章 递归:File类: 1.1:概述 java.io.File 类是文件和目录路径名的抽象表示,主要用于文件和目录的创建.查找和删除等操作. ...

  3. 一文读懂机器学习中的模型偏差

    一文读懂机器学习中的模型偏差 http://blog.sina.com.cn/s/blog_cfa68e330102yz2c.html 在人工智能(AI)和机器学习(ML)领域,将预测模型参与决策过程 ...

  4. 一文读懂密码学中的证书

    一文读懂密码学中的证书 之前的文章中,我们讲到了数字签名,数字签名的作用就是防止篡改和伪装,并且能够防止否认.但是要正确运用数字签名技术还有一个非常大的前提,那就是用来验证签名的公钥必须真正的属于发送 ...

  5. 一文读懂SpringBoot中的事件机制

    一文读懂SpringBoot中的事件机制?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法. 要"监听"事件,我们总是 ...

  6. 前端面试必会 | 一文读懂 JavaScript 中的 this 关键字

    this 是一个令无数 JavaScript 编程者又爱又恨的知识点.它的重要性毋庸置疑,然而真正想掌握它却并非易事.希望本文可以帮助大家理解 this. JavaScript 中的 this Jav ...

  7. 带你一文读懂Javascript中ES6的Symbol

    带你一文读懂Javascript中ES6的Symbol 前言 基础类型 Symbol Symbol.for 与 Symbol.keyFor Symbol.iterator Symbol.search ...

  8. 一文读懂机器学习中奇异值分解SVD

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 目录: 矩阵分解 1.1 矩阵分解作用 1.2 矩阵分解的方法一文 ...

  9. 一文读懂信息安全中的恶意代码、病毒、木马、蠕虫......

    一文读懂信息安全中的恶意代码.病毒.木马.蠕虫...... 病毒:破坏计算机功能或数据,以破坏为主,传染其他程序的方式是通过修改其他程序来把自身或其变种复制进去完成的,典型的熊猫烧香 蠕虫:通过网络的 ...

最新文章

  1. java中的super限定
  2. 对java多线程里Synchronized的思考
  3. Mac OS 上安装 PostgreSQL
  4. php打印四行三列表格,php打印数组_php数组实例之表格状打印
  5. 苏宁 11.11:仓库内多 AGV 协作的全局路径规划算法研究
  6. lucene 高亮显示
  7. 微信小程序周报(第八期)
  8. CISCO ASA防火墙配置实验
  9. 安卓滚动字幕以及TextSwitcher、ImageSwitcher使用
  10. JAVE 视音频转码
  11. 用计算机弹让我做你的眼睛,让我做你的眼睛 (改编版)歌词
  12. ArcGIS利用DEM提取河流水系(附练习数据下载)
  13. Jeecg-Boot 使用技巧
  14. 解决yum安装gcc报错:Error: Package: glibc-headers-2.17-317.el7.x86_64
  15. 计算机硕士论文解读,计算机专业硕士论文提纲范文大全 计算机专业硕士论文提纲如何写...
  16. Vue + DataV + SignalR 数字化大屏展示
  17. Docusaurus 搭建个人博客(支持离线搜索)
  18. v-bind绑定无效 - Vue
  19. “PMVDN”元宇宙新势力崛起
  20. 2020陕西初中学业水平计算机,2020年陕西省初中学业水平考试·模拟卷(一)...

热门文章

  1. win10 系统创建环回接口步骤
  2. STM32学习之CAN控制器简介
  3. ReactHooks——制作一个简单的瀑布流(css)
  4. 基于协同过滤的算法 图书推荐系统
  5. google pixel 手机使用magisk获取root权限
  6. 人源肿瘤细胞异种移植(CDX)小鼠模型 CDX模型的优势简介
  7. java.lang.IllegalStateException: Failure saving state: active HomeFragment has cleared index: -1
  8. python_获取文件及文件夹大小修改时间
  9. P2P模式分析之资金池与第三方托管
  10. 一个整数称为完全平方数,是指它的值是另一个整数的平方。如81是个完全平方数,因为它是9的平方。请按要求完成:①在三位的正整数中寻找所有完全平方数;例如:144(12*12),676(26*26)