当前位置: 首页 > news >正文

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

// 父类
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执行抽象类的析构函数!
执行子类的析构函数!
执行抽象类的析构函数!


http://www.taodudu.cc/news/show-4565074.html

相关文章:

  • 【physx/wasm】在physx中添加自定义接口并重新编译wasm
  • excel---常用操作
  • Lora训练Windows[笔记]
  • linux基础指令讲解(ls、pwd、cd、touch、mkdir)
  • InnoDB 事务处理机制
  • 启明云端ESP32 C3 模组WT32C3通过 MQTT 连接 AWS
  • 一文读懂之java组合
  • 读书笔记21:解释器模式
  • 【Python】读书笔记:Python基础教程-项目1-即时标记
  • java核心技术读书笔记—继承
  • “Let’s Eat Grandma”:标点符号(句法树)增强语义表达,用于情感分析
  • Java编程思想读书笔记——第十章:内部类
  • Python基础教程(第2版)读书笔记
  • 基于安卓的公司员工考勤系统的设计与实现
  • 职工考勤管理系统
  • 员工考勤管理系统html,一种员工考勤管理系统的制作方法
  • 内存的类型
  • Linux命令_Note1
  • 计算机体系结构——内存
  • 《Unity Shader入门精要》笔记02 第1章+第2章
  • 30天自制OS学习笔记 (四)C语言与画面显示的练习
  • 自制操作系统日志——第四天
  • Linux Graphics 周刊(第 5 期)
  • unity3D 移动平台性能优化
  • 计算机外围设备
  • 轻量级模型设计/部署
  • 30天自制操作系统学习-第8天
  • 【操作系统】30天自制操作系统--(9)叠加处理
  • C语言VRAM字符串平滑移动,航空数字化仪表中动画显示技术的应用
  • 第4天:C语言与画面显示的练习
  • 显卡 内存分配 linux,【原创】Linux环境下的图形系统和AMD R600显卡编程(4)——AMD显卡显存管理机制...
  • 用C语言开发NES游戏(CC65)03、VRAM缓冲区
  • dm8148 开发只boot启动参数vram=128简介
  • 虚拟机服务器CPU授权,vSphere5全新的许可授权方式——CPU许可+vRAM授权
  • 8位色320*200分辨率下的屏幕坐标与VRAM地址计算
  • VRAM