C#派生类中构造函数的执行

目录

一、派生类构造函数执行顺序

二、构造函数初始化语句

1. 使用关键字base并指明使用哪一个基类构造函数

2. 使用this关键字并指明应该使用当前类的哪一个构造函数


一、派生类构造函数执行顺序

针对派生类,创建对象的基类部分过程中会隐式调用基类的某一个构造函数作为创建实例过程的一部分,在继承层次链中的每个类在创建对象过程中,执行自己的构造函数体之前都要执行它的基类构造函数。

也就是说,如果派生类的基类也是派生类,则每个派生类只需负责其直接基类的构造,不负责简介基类的构造,并且实例构造函数的顺序是从最上面的基类开始下溯的,静态构造函数是从下面的类开始上溯的(下面代码解释了这个问题)。

注意,在构造函数中要避免调用虚函数方法。在执行基类构造函数是,在执行派生类的构造函数方法体,基类的虚方法会调用派生类的覆写方法,因此调用会在派生类没有完全初始化之前传递到派生类。

图一  派生类实例化过程 执行顺序

 

/*代码功能:讲述上图派生类构造函数的执行顺序(包含各层次类都静态构造、实例构造函数)
 * 时间:2018年8月15日23点35分
 */
    public class MyBaseClass
    {
        static MyBaseClass()                              //4. 第一个类的静态构造函数执行
        {
            Console.WriteLine("Static:MyBaseClass!");
        }
        public MyBaseClass()                              //5. 第一个类的实例构造函数执行
        {
            Console.WriteLine("MyBaseClass!");
        }
    }

    public class MyDrivedClass : MyBaseClass
    {
        static MyDrivedClass()                            //3. 第二个类的静态构造函数执行
        {
            Console.WriteLine("Static:MyDrivedClass");
        }
        public MyDrivedClass()                            //6. 第二个类的实例构造函数执行
        {
            Console.WriteLine("MyDrivedClass!");
        }

    }

    public class MyThirdClass : MyDrivedClass
    {
        public int i = 5;                                
        public string str = "value";                      //1. 首先,进行成员初始化

        static MyThirdClass()                             //2. 第三个类的静态构造函数执行
        {
            Console.WriteLine("Static:MyThirdClass!");
        }
        public MyThirdClass()                             //7. 第三个类的构造函数执行
        {
            Console.WriteLine("MyThirdClass!");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            MyThirdClass f1 = new MyThirdClass();        //0. 实例化对象
        }
    }

/* 程序运行结果:
-----------------------------
Static:MyThirdClass!
Static:MyDrivedClass
Static:MyBaseClass!
MyBaseClass!
MyDrivedClass!
MyThirdClass!
-----------------------------
*/

二、构造函数初始化语句

一般情况下在实例化派生类对象的时候都会调用基类的无参数构造函数,但是构造函数是可以重载的,所以基类可能有多个构造函数。根据我的思考,我们在显示的创建有参数构造函数的时候,这样系统就不会自动创建默认无参数构造函数,所以我们在创建有参数构造函数的时候,要同时再显示创建一个无参数的和默认形式相同的构造函数,这样设计才是优雅的设计。

当声明一个不带构造函数初始化语句的构造函数时,其实是省略了base()构造函数初始化语句,如下两个形式是等价的:

/* 等价的构造函数初始化语句 */

class DrivedClass : BaseClass
{
    DrivedClass()                //隐式使用基类构造函数BaseClass()的构造函数
    {
        ...
    }
}

class DrivedClass : BaseClass
{
    DrivedClass() : base()      //显示使用基类构造函数BaseClass()的构造函数
    {
        ...
    }
}

如果想要派生类使用指定的基类构造函数然不是默认的无参数构造函数,可以使用“构造函数初始化语句”来进行指定。可以从以下两个形式:

1. 使用关键字base并指明使用哪一个基类构造函数

1. 作用:使用base()指明使用基类的哪一个构造函数;

2. 用法:使用有关键字base的构造函数初始化语句和要调用的基类构造函数参数列表

        /*代码功能:讲述在派生类中如何使用关键字base指明使用哪一个构造函数
         * 时间:2018年8月16日09点44分
         */
    public class BaseClass
    {
        public BaseClass()                                  //参见前文,虽然用不到无参构造,但这是优雅的写法
        {
            Console.WriteLine("无参数基类构造函数!");
        }

        public BaseClass(string str1)                                   
        {
            Console.WriteLine("带参数基类构造函数,参数为: {0}",str1);
        }
    }

    public class DrivedClass : BaseClass
    {
        public DrivedClass()
        {
            Console.WriteLine("无参数派生类构造函数!");
        }

        /* 利用base(str2)指定使用参数为一个string类型的基类构造函数 */
        public DrivedClass(string str2) : base(str2)        //base()里的参数,前面DrivedClass里写什么,这里就对应写什么即可
        {
            Console.WriteLine("带参数派生类构造函数,参数为: {0}",str2);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            string str = "name";
            DrivedClass f1 = new DrivedClass(str);
        }
    }

/* 程序运行结果:
-----------------------------
带参数基类构造函数,参数为: name
带参数派生类构造函数,参数为: name
-----------------------------
*/

2. 使用this关键字并指明应该使用当前类的哪一个构造函数

1. 作用:使用this关键字可以让构造过程中使用当前类中的其他构造函数。

2. 推荐应用(公共构造函数):若一个类有多个构造函数,并且它们都要在对象构造的过程开始时执行一些公共的代码。因此,将公共的代码提取出来作为一个构造函数,其他所有的构造函数将其作为构造函数初始化语句使用,这样减少了了类中的重复代码,推荐做法。

3. 公共构造函数的访问级别:如果该构造函数能完全初始化一个对象的内容,使用public访问级别;如果该构造函数不能完全初始化一个对象的所有内容,使用private访问级别(禁止外部调用导致只初始化对象的一部分)。

4. 注意:针对第二条,你可能感觉完全可以通过让所有构造函数来调用这个公共构造函数来实现执行公共代码,这不是好的设计策略。有些事情必须在构造函数中进行,其他地方不行,例如readonly字段只可以在构造函数中初始化,如果试图用调用构造函数来初始化readonly字段,编译器知道该方法是构造函数,会自动进行优化并产生一个编译错误。

    /*代码功能:讲述在派生类中如何使用关键字this使用当前类中的其他构造函数
     * 时间:2018年8月16日10点42分
     */
    public class MyClass
    {
        public string Name { get; private set; }
        public int ID { get; private set; }
        public int Age { get; private set; }
        public int Score { get; private set; }
        
        /* 推荐的优雅写法,虽然该构造函数没参数 */
        public MyClass()                
        {

        }

        /* 带参数构造函数,功能是初始化类内参数,意图是作为公共的执行代码以防冗余 */
        public MyClass(int i,int j)                  
        {
            Age = i;
            Score = j;
        }

        /* 带this的构造函数,目的是避免大括号面再赋值一遍Age\Score这两个参数 */
        public MyClass(string name) : this(20,90)                           
        {
            Name = name;
        }

        /* 带this的构造函数,目的是避免再赋值一遍ID\Age\Score这三个参数 */
        public MyClass(int id) : this("李四")
        {
            ID = id;
        }
        
        //输出类内所有参数
        public void Print()
        {
            Console.WriteLine("姓名:{0}\n标识:{1}\n年龄:{2}\n成绩:{3}\n",Name,ID,Age,Score);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            string name = "张三";
            int id = 123456789;

            MyClass f1 = new MyClass(name);    //使用带有一个string类型参数的构造函数实例化对象
            f1.Print();

            MyClass f2 = new MyClass(id);      //使用带有一个int类型参数的构造函数实例化对象
            f2.Print();
        }
    }

/* 程序运行结果:
-----------------------------
姓名:张三
标识:0
年龄:20
成绩:90

姓名:李四
标识:123456789
年龄:20
成绩:90

-----------------------------
*/

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值