一文读懂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执行抽象类的析构函数! 执行子类的析构函数! 执行抽象类的析构函数!
相关文章
- Python虚拟环境很简单,看完你就会了
- 总结几个简单好用的Python人脸识别算法
- 又是好兄弟 爱立信与三星和解专利纠纷
- Flink分布式程序的异常处理
- 好用的 Python 虚拟环境,没有之一
- 为什么TensorFlow可以做机器学习开发?
- 记一次beego通过go get命令后找不到bee.exe的坑
- 指定Task任务顺序执行
- CompletionService 使用小结
- Win环境安装Protobuf 2.0 版本
- shell 同时执行多任务下载视频
- Seata 分布式事务解决方案及特点
- zookeeper 使用api 进行节点增删改查及实现简易的配置中心
- 潘石屹首次Python考试成绩 99 分,失分点:画完图后忘了隐藏画笔的箭头
- 啃完Python基础,你要这样做才行
- Python开发者调查显示只有十分之一的人还在用Python 2
- TIOBE 5 月编程语言排行榜:C语言跑到第一,python连续两年上涨
- Python 是前浪,Julia 是后浪?
- 不朽 C++ 为新贵 Python 应用提速 8000 倍!
- 3个Python函数帮程序员们避免编写循环,提高运行速度