首先举个例子:
运行结果是
namespace 继承
{
class Program
{
static void Main(string[] args)
{
China joey = new China(); //第一个中国人,首先会调用People的无参构造函数,然后再调用China类自己的无参构造函数
China lidashu = new China(2); //如果在 China 类的带1个参数的构造函数 后面 没写base,则先调用People的无参构造函数,然后是China的有参 。
//如果有写 base ,则先调用 People的带参数的构造函数,再是China的有参
China xiaoming = new China("小明", 3);
}
}
class People
{
public People() //父类的无参构造函数
{
Console.WriteLine("默认的无参构造函数");
}
public People(int i)
{
Console.WriteLine("People带1个参数的构造函数");
}
}
class China : People
{
public China()//等价于 public China():base() 表示这个China的无参构造函数 会隐式调用 People()这个无参数的构造函数
{
Console.WriteLine("创建了一个中国人");
}
public China(int num)
//: base(num) //如果这里有写 base(num) 则调用People类的 People(int i) 这个构造函数,如果没有写base,那么还是 隐式调用People()这个无参数的构造函数
{
Console.WriteLine(num + "个中国人");
}
public China(string name, int num): base(num) //虽然China子类的构造参数的个数同People父类不一样,但是没关系,一样可以传值到父类进行调用父类的构造参数
//至于是调用父类的哪个构造参数,就要看这里的 base 后面有几个参数,并且要看传入的是什么值类型
{
Console.WriteLine(num + "个中国人,名字是" + name);
}
}
}
结论:
1:继承的写法 用冒号 : 来表示继承 (接口也是用 冒号 : 来表示的,这个要注意)。子类是调用父类的构造函数,而不是继承了父类的构造函数.
2: 如果子类,在写构造参数的时候,如果没有写 :base() 则是隐式的调用父类的 无参数的构造函数(如果父类没有无参数的构造函数,则什么都不干,如果父类,只有带参数的构造函数,而没有无参数的构造函数,则子类在写构造函数的时候,必须写 base(num),这是因为,父类已经没有无参的构造函数了,但是子类总是会调用A的构造参数的,这个时候我们的父类没有无参构造函数,所以你子类必须显式的来用base来告诉是调用父类的哪个有参。如果父类根本就没有构造函数,则子类就无所谓了,因为此时相当于父类是有一个空的无参构造函数)
如果有写 base ,则根据 base 后面的挂号里面的参数来调用父类的构造参数,例如 base() 这个是调用父类默认的无参构造,如果是 base(j) ,则是父类的1个参数的构造参数
3:特殊的情况,子类的构造参数,有多个参数,例如 public China(string name, int num): base(num) ,这个表示 子类有2个参数,调用父类的一个带参的构造函数,也就是说,调用父类多少个构造参数,是由后面的base挂号里面的参数来决定的,如果是base() 或者没有写,就是调用父类默认的无参构造函数。如果是多个,则匹配父类同样个数的有参构造函数
如果子类里面,有自带的字段呢?
namespace 继承
{
class Program
{
static void Main(string[] args)
{
B b = new B();
//这个运行的结果是 打印下面的2行
//x=1,y=0
//x=1,y=-1
}
}
class A
{
public A()
{
PrintFields(); //步骤2:在调用A的构造的时候,因为这是个虚方法,而且子类又用 override 进行了重写,所以会去调用子类的 override 方法
}
public virtual void PrintFields() { }
}
class B : A
{
int x = 1;
int y; //步骤1:在b实例化的时候,会先运行 x=1,y=0的赋值,然后开始执行B的构造的时候,先去调用A的默认构造
public B() //步骤4: 父类的构造函数完成之后,开始子类的构造方法,执行这个输出语句
{
y = -1;
Console.WriteLine("x={0},y={1}", x, y);
}
public override void PrintFields() //步骤3 : 运行这个方法
{
Console.WriteLine("x={0},y={1}", x, y);
}
}
}
4:如果子类有字段,那么会先给字段赋值,再执行构造函数
5:如果父类有 virtual 函数,但是子类 没有写 override 来进行重写的话,那么子类这个函数是不执行的。(父类的会执行,但是子类不执行)
例如
class Program { static void Main(string[] args) { B b = new B(); //这个运行的结果是 打印下面的1行 //x=1,y=-1 } } class A { public A() { PrintFields(); //2:在调用A的构造的时候,虽然这是个虚方法,但是子类没有写 override 进行重写,所以不执行子类 } public virtual void PrintFields() { } //3:因为子类没有 override ,所以不执行子类的重写,而是会运行这里的空的函数 } class B : A { int x = 1; int y; //1:在b实例化的时候,会先运行 x=1,y=0的赋值,然后开始执行B的构造的时候,先去调用A的默认构造 public B() //4: 父类的构造函数完成之后,开始子类的构造方法,执行这个输出语句 { y = -1; Console.WriteLine("x={0},y={1}", x, y); } public void PrintFields() //因为这里没有写 override 所以这个函数从头到尾压根都没有执行·· { Console.WriteLine("x={0},y={1}", x, y); } }
关于base的用法
–1.调用父类中非私有的成员(调用成员,父类), base点不出子类独有成员。
–2.调用父类中的构造函数(调用构造函数,父类)
–
•当调用从父类中继承过来的成员的时候,如果子类没有重写则this.成员;那么this.成员与base.成员,没有区别,都是指的父类的成员。
如果子类重写了父类成员,则this.成员;调用的是子类重写以后的成员。 而base.成员;调用的依然是父类的成员。
•
•子类构造函数默认调用父类的无参构造函数;
•如果父类没有无参构造函数,则必须指明调用父类哪个构造函数