c#问答篇:继承类变量初始化和构造器执行的过程
我们知道,当子类继承自父类时,为了保证父类成员变量的正确初始化,子类的任何构造器默认的都必须调用父类的某一构造器,具体调用哪个构造器要看构造器的初始化参数列表。如果没有初始化参数列表,那么子类的该构造器就调用父类的无参数构造器;如果有初始化参数列表,那么子类的该构造器就调用父类对应的参数构造器。
而构造器的作用就在于负责类中成员变量(域)的初始化;同时类内的成员变量声明初始化被编译器转换成赋值语句强加在类的每一个构造器的内部。那么子类中初始化语句与调用父类构造器的语句的顺序是什么呢?为了得出结论,我们考虑如下代码(test.cs):
运行的结果你通过看代码可能就已经知道了:),而且你也可能已经猜测到了:子类初始化语句在父类构造器调用之前,最后执行的是本构造器内的语句。
我们通过IL代码来分析和验证这一执行过程:编译test.cs,然后运行ildasm,打开test.exe文件...
而构造器的作用就在于负责类中成员变量(域)的初始化;同时类内的成员变量声明初始化被编译器转换成赋值语句强加在类的每一个构造器的内部。那么子类中初始化语句与调用父类构造器的语句的顺序是什么呢?为了得出结论,我们考虑如下代码(test.cs):
using System;
public class MyClass1
{
public MyClass1()
{
Console.WriteLine("first,MyClass2 to MyClass1's ctor!");
Console.WriteLine("then,virtual Print() of MyClass1 call instance override Print() of MyClass2!");
Print();
}
public virtual void Print() {}
}
public class MyClass2: MyClass1
{
int x = 1;
int y; //zero,初始化成员变量x=1 y=0
public MyClass2()
{
Console.WriteLine("3th,go to inter of MyClass2's cotr!");
y = -1;
Console.WriteLine("4th,call instance override Print() of MyClass2 self!");
Print();
}
public override void Print()
{
Console.WriteLine("MyClass2: x = {0}, y = {1}", x, y);
}
}
public class Test
{
static void Main()
{
MyClass2 MyObject1 = new MyClass2();
}
}
public class MyClass1
{
public MyClass1()
{
Console.WriteLine("first,MyClass2 to MyClass1's ctor!");
Console.WriteLine("then,virtual Print() of MyClass1 call instance override Print() of MyClass2!");
Print();
}
public virtual void Print() {}
}
public class MyClass2: MyClass1
{
int x = 1;
int y; //zero,初始化成员变量x=1 y=0
public MyClass2()
{
Console.WriteLine("3th,go to inter of MyClass2's cotr!");
y = -1;
Console.WriteLine("4th,call instance override Print() of MyClass2 self!");
Print();
}
public override void Print()
{
Console.WriteLine("MyClass2: x = {0}, y = {1}", x, y);
}
}
public class Test
{
static void Main()
{
MyClass2 MyObject1 = new MyClass2();
}
}
我们通过IL代码来分析和验证这一执行过程:编译test.cs,然后运行ildasm,打开test.exe文件...
//Test.ctor()
IL_0001: call instance void [mscorlib]System.Object::.ctor()
//Test.Main()
.method private hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 1
.locals init (class MyClass2 V_0)
IL_0000: newobj instance void MyClass2::.ctor()
//...
}
IL_0001: call instance void [mscorlib]System.Object::.ctor()
//Test.Main()
.method private hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 1
.locals init (class MyClass2 V_0)
IL_0000: newobj instance void MyClass2::.ctor()
//...
}
//MyClass2.ctor()
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// 代码大小 47 (0x2f)
.maxstack 2
IL_0000: ldarg.0
IL_0001: ldc.i4.1
IL_0002: stfld int32 MyClass2::x
IL_0007: ldarg.0
IL_0008: call instance void MyClass1::.ctor()
IL_000d: ldstr "3th,go to inter of MyClass2's cotr!"
IL_0012: call void [mscorlib]System.Console::WriteLine(string)
IL_0017: ldarg.0
IL_0018: ldc.i4.m1
IL_0019: stfld int32 MyClass2::y
IL_001e: ldstr "4th,call instance override Print() of MyClass2 self!"
IL_0023: call void [mscorlib]System.Console::WriteLine(string)
IL_0028: ldarg.0
IL_0029: callvirt instance void MyClass1::Print()
IL_002e: ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// 代码大小 47 (0x2f)
.maxstack 2
IL_0000: ldarg.0
IL_0001: ldc.i4.1
IL_0002: stfld int32 MyClass2::x
IL_0007: ldarg.0
IL_0008: call instance void MyClass1::.ctor()
IL_000d: ldstr "3th,go to inter of MyClass2's cotr!"
IL_0012: call void [mscorlib]System.Console::WriteLine(string)
IL_0017: ldarg.0
IL_0018: ldc.i4.m1
IL_0019: stfld int32 MyClass2::y
IL_001e: ldstr "4th,call instance override Print() of MyClass2 self!"
IL_0023: call void [mscorlib]System.Console::WriteLine(string)
IL_0028: ldarg.0
IL_0029: callvirt instance void MyClass1::Print()
IL_002e: ret
}
//MyClass1.ctor()
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// 代码大小 33 (0x21)
.maxstack 1
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ldstr "first,MyClass2 to MyClass1!"
IL_000b: call void [mscorlib]System.Console::WriteLine(string)
IL_0010: ldstr "then,virtual Print() of MyClass1 call instance ove"
+ "rride Print() of MyClass2!"
IL_0015: call void [mscorlib]System.Console::WriteLine(string)
IL_001a: ldarg.0
IL_001b: callvirt instance void MyClass1::Print()
IL_0020: ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// 代码大小 33 (0x21)
.maxstack 1
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ldstr "first,MyClass2 to MyClass1!"
IL_000b: call void [mscorlib]System.Console::WriteLine(string)
IL_0010: ldstr "then,virtual Print() of MyClass1 call instance ove"
+ "rride Print() of MyClass2!"
IL_0015: call void [mscorlib]System.Console::WriteLine(string)
IL_001a: ldarg.0
IL_001b: callvirt instance void MyClass1::Print()
IL_0020: ret
}
不用添加注释,我想大家都已经明白了——继承类变量初始化和构造器执行的过程是:首先对子类的变量初始化(这里的y初始没有赋值,取默认值0),然后调用父类构造器,最后执行本构造器内的语句。变量初始化的优先权是最高的!
<本文适于初学者,以“C#锐利体验之构造器与析构器”为参考>
<本文适于初学者,以“C#锐利体验之构造器与析构器”为参考>