一、C#构造函数调用顺序
定义基类MyBase,定义派生类MyDerived继承自MyBase。
//定义基类
class MyBase
{
private string name; //基类字段
}
//定义派生类
class MyDerived :MyBase
{
private int age; //派生类字段
}
这里未填写构造函数,在实例化时会生成默认构造函数,默认构造函数均用public修饰,无参数。默认构造函数会对类中的变量使用默认值进行实例化(int型字段会初始为0,string类型字段会初始为null)。在实例化MyDerived时首先会调用基类构造函数,然后调用派生类构造函数。另外C#中所有类默认派生自System.Object。
所以这里的调用顺序是:
System.Object();
MyBase();
MyDerived();
二、使用自定义的构造函数
1.自定义无参构造函数
这里基类使用自定义无参数的构造函数,将基类字段name初始化为“”,并在基类构造函数中输出一句话。派生类依然不填写构造函数,使用生成的默认构造函数。
//定义基类
class MyBase
{
private string name; //基类字段
public MyBase()
{
name = "";
Console.WriteLine("这是基类的构造函数");
}
}
//定义派生类
class MyDerived :MyBase
{
private int age; //派生类字段
}
输出结果:这是基类的构造函数。
分析总结:此时调用顺序依然是先基类构造函数,这里会使用自定义的构造函数。派生类中依然使用默认构造函数。
2.修改基类构造函数
在这里如果将基类修改为如下形式将会在派生类产生一个错误。
(1)构造函数声明为private
class MyBase
{
private string name; //基类字段
private MyBase()
{
name = "";
Console.WriteLine("这是基类的构造函数");
}
}
错误:“MyBase.MyBase()”不可访问,因为它具有一定的保护级别
(2)构造函数带有参数
class MyBase
{
private string name; //基类字段
public MyBase(string name)
{
name = "";
Console.WriteLine("这是基类的构造函数");
}
}
错误:未提供与“MyBase.MyBase(string)”的必须形参“name”对应的实参
分析总结:这些错误说明编译器在为派生类生成默认构造函数的时候,需要使用基类构造函数,并且该基类必须存在public修饰的的且无参数的构造函数。
3.自定义带参数的构造函数
class MyBase
{
private string name; //基类字段
public MyBase(string name) //带有参数的基类构造函数
{
this.name =name ;
Console.WriteLine("这是基类的构造函数");
}
}
以下的写法均会产生错误:
(1)使用系统提供的默认构造函数
class MyDerived :MyBase
{
private int age; //派生类字段
}
(2)自定义无参构造函数
class MyDerived :MyBase
{
private int age; //派生类字段
public MyDerived()
{
age = 0;
Console.WriteLine("这是派生类的构造函数");
}
}
(3)定义有参数构造函数,但未向基类传递参数
class MyDerived :MyBase
{
private int age; //派生类字段
public MyDerived(string name)
{
age = 0;
Console.WriteLine("这是派生类的构造函数");
}
}
(4)存在多个构造函数,但部分构造函数未向基类传递参数
class MyDerived :MyBase
{
private int age; //派生类字段
public MyDerived()
{
age = 0;
Console.WriteLine("这是派生类的构造函数");
}
public MyDerived(string name) : base(name)
{
age = 0;
Console.WriteLine("这是派生类的构造函数");
}
}
以上四种情况会产生的相同的错误:未提供与“MyBase.MyBase(string)”的必需形参“name”对应的实参(第四种错误提示在无参构造函数MyDerived()上)
正确的写法:
(1)通过派生类的构造函数传递可变的参数到基类构造函数
class MyDerived :MyBase
{
private int age; //派生类字段
public MyDerived(string name) : base(name)
{
age = 0;
Console.WriteLine("这是派生类的构造函数");
} }
(2)在派生类构造函数后添加base(“aaa”)传入固定的参数
class MyDerived :MyBase
{
private int age; //派生类字段
public MyDerived():base("aaa")
{
age = 0;
Console.WriteLine("这是派生类的构造函数");
}
}
分析总结:如果基类中使用了带参数构造函数时,并且只有这样一个构造函数,那么我们在派生类中必须使用自定义的构造函数,并且使用base()向基类的构造函数传递参数。在这两种写法中第一种可以在派生类实例化时传入参数,并将该参数传递到基类。第二种则可以写在任意派生类构造函数后,将默认的值传到基类构造函数中。
//定义基类
class MyBase
{
private string name; //基类字段
public MyBase(string name)
{
this.name =name ;
Console.WriteLine("这是基类的构造函数"+name);
}
}
//定义派生类
class MyDerived :MyBase
{
private int age; //派生类字段
public MyDerived():base("aaa")
{
age = 0;
Console.WriteLine("这是派生类的构造函数");
}
public MyDerived(string name) : base(name)
{
age = 0;
Console.WriteLine("这是派生类的构造函数");
}
}
class Program
{
static void Main(string[] args)
{
MyDerived test = new MyDerived();
MyDerived test2 = new MyDerived("bbb");
}
}
执行结果:
这是基类的构造函数aaa
这是派生类的构造函数
这是基类的构造函数bbb
这是派生类的构造函数
三、构造函数中的this和base关键字
this和base均为构造函数后使用的关键字,他们的语法基本相同,均为写在构造函数后面。
修饰符 构造函数名() : this/base([参数1],[参数2]...)。
this先执行this指向的构造函数,执行完成后执行当前的构造函数。base表示先执行基类的构造函数,执行完成后执行本构造函数。如果具有多个构造函数,this和base执行的内容根据后面所指定的参数决定。
//定义基类
class MyBase
{
private string name; //基类字段
public MyBase(string name)
{
this.name = name;
Console.WriteLine("这是基类的带参数的构造函数,name is:" + name);
}
public MyBase()
{
Console.WriteLine("这是基类的无参数的构造函数");
}
}
//定义派生类
class MyDerived : MyBase
{
private int age; //派生类字段
public MyDerived(string name) : base(name) //先调用基类的一个参数的构造函数,传入name
{
Console.WriteLine("这是派生类的一个参数的构造函数,向基类传递参数name");
}
public MyDerived(string name,int age) : this(name) //先执行本类的无参构造函数
{
this.age = age;
Console.WriteLine("这是派生类的有两个参数的构造函数,传入参数name和age,age is:"+age);
}
}
class Program
{
static void Main(string[] args)
{
MyDerived test = new MyDerived("AAA",18);
}
}
在这里进行实例化时首先会进入派生类两个参数的构造函数,派生类进入后会优先执行this指向的一个参数的构造函数,此时会先调用基类的含有一个参数的构造函数。
执行结果:
这是基类的带参数的构造函数,name is:AAA
这是派生类的一个参数的构造函数,向基类传递参数name
这是派生类的有两个参数的构造函数,传入参数name和age,age is:18
namespace baseknowledge
{
//定义基类
class MyBase
{
private string name; //基类字段
public MyBase(string name)
{
this.name = name;
Console.WriteLine("这是基类的带参数的构造函数,name is:" + name);
}
public MyBase()
{
Console.WriteLine("这是基类的无参数的构造函数");
}
}
//定义派生类
class MyDerived : MyBase
{
private int age; //派生类字段
public MyDerived(string name)
{
Console.WriteLine("这是派生类的一个参数的构造函数");
}
public MyDerived(string name, int age) : this(name) //先执行本类的无参构造函数
{
this.age = age;
Console.WriteLine("这是派生类的有两个参数的构造函数,传入参数name和age,age is:" + age);
}
}
class Program
{
static void Main(string[] args)
{
MyDerived test = new MyDerived("AAA", 18);
}
}
}
这里在派生类中没有指定需要执行构造函数,那么首先会执行基类没有参数的构造函数。然后执行派生类两个参数的构造函数后this指定的一个参数的构造函数,最后执行两个参数的构造函数。
执行结果:
这是基类的无参数的构造函数
这是派生类的一个参数的构造函数
这是派生类的有两个参数的构造函数,传入参数name和age,age is:18
分析总结:通过以上两段代码可以发现,执行派生类的构造函数时首先执行基类的构造函数,如果存在base指定的基类构造函数,执行指定的构造函数,没有则执行默认的构造函数。执行结束后执行派生类构造函数。同时this和base指定的构造函数在当前构造函数之前执行。