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

Posted 魏晓蕾

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一文读懂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执行抽象类的析构函数!
执行子类的析构函数!
执行抽象类的析构函数!

以上是关于一文读懂C#中的抽象类抽象方法virtual虚函数override重写函数及父类子类构造函数和析构函数的执行顺序的主要内容,如果未能解决你的问题,请参考以下文章

C#虚方法(Virtual)抽象方法(Abstract),接口(Interface)抽象类的区别

C# 抽象方法和虚方法的区别

虚方法virtual抽象方法abstract接口interface区别

c#多态之抽象类与虚方法的异同点~

c#中啥情况用abstract,啥情况下用virtual

C#中的抽象类抽象方法和虚方法