c#问答篇:继承类变量初始化和构造器执行的过程

我们知道,当子类继承自父类时,为了保证父类成员变量的正确初始化,子类的任何构造器默认的都必须调用父类的某一构造器,具体调用哪个构造器要看构造器的初始化参数列表。如果没有初始化参数列表,那么子类的该构造器就调用父类的无参数构造器;如果有初始化参数列表,那么子类的该构造器就调用父类对应的参数构造器。

而构造器的作用就在于负责类中成员变量(域)的初始化;同时类内的成员变量声明初始化被编译器转换成赋值语句强加在类的每一个构造器的内部。那么子类中初始化语句与调用父类构造器的语句的顺序是什么呢?为了得出结论,我们考虑如下代码(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();
    }

}
运行的结果你通过看代码可能就已经知道了:),而且你也可能已经猜测到了:子类初始化语句在父类构造器调用之前,最后执行的是本构造器内的语句。
我们通过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()
  //...
}
//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
}
//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
}
不用添加注释,我想大家都已经明白了——继承类变量初始化和构造器执行的过程是:首先对子类的变量初始化(这里的y初始没有赋值,取默认值0),然后调用父类构造器,最后执行本构造器内的语句。变量初始化的优先权是最高的!

<本文适于初学者,以“C#锐利体验之构造器与析构器”为参考>

posted on 2004-08-03 11:31  9yue  阅读(2299)  评论(0编辑  收藏  举报