C#中类的静态构造函数(cctor)由谁调用?
测试代码
// See https://aka.ms/new-console-template for more information Console.WriteLine("Hello, World!"); B b = new B(); public class A { static A() { Console.WriteLine("Static ctor A"); } public A() { Console.WriteLine("ctor A"); } } public class B:A { static B() { Console.WriteLine("Static ctor B"); } public B() { Console.WriteLine("ctor B"); } }
程序输出
Static ctor B
Static ctor A
ctor A
ctor B
疑问与观察
我的问题是:谁调用了静态构造函数?搜索资料得到的答案是“系统(CLR)会在类的任何实例出现前调用静态构造函数”,但查看IL代码后仍找不到静态构造函数(称为cctor)的调用线索。
Main方法的IL代码
.method private hidebysig static void '<Main>$' ( string[] args ) cil managed { // Method begins at RVA 0x2050 // Header size: 12 // Code size: 18 (0x12) .maxstack 1 .entrypoint .locals init ( [0] class B b ) IL_0000: ldstr "Hello, World!" IL_0005: call void [System.Console]System.Console::WriteLine(string) IL_000a: nop IL_000b: newobj instance void B::.ctor() IL_0010: stloc.0 IL_0011: ret } // end of method Program::'<Main>$'
在Main()中调用了B::.ctor(),B::.ctor()中又调用了A::.ctor(),但我未发现任何cctor的调用。我最初猜测是在.locals init阶段调用,但测试后证明并非如此。之后我猜想可能是构造函数中的ldarg.0导致的:
B类实例构造函数的IL代码
.method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x20a8 // Header size: 1 // Code size: 20 (0x14) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void A::.ctor() IL_0006: nop IL_0007: nop IL_0008: ldstr "ctor B" IL_000d: call void [System.Console]System.Console::WriteLine(string) IL_0012: nop IL_0013: ret } // end of method B::.ctor
以上是B::.ctor方法,第一行是ldarg.0,据我所知ldarg.0指代“this”,这是否意味着已经存在类的实例?是否为了引用“this”,CLR才调用了静态构造函数?我想明确C#中到底是谁调用了cctor。
解答
静态构造函数(cctor)的调用是CLR运行时自动触发的行为,不会在编译生成的IL代码里显式出现调用指令。
当执行newobj instance void B::.ctor()这条IL指令时,CLR会在真正执行实例构造函数前完成以下步骤:
- 检查B类的静态构造函数是否已执行,若未执行则先调用B的cctor;
- 由于B继承自A,CLR会继续检查A的静态构造函数执行状态,未执行则调用A的cctor;
- 等所有依赖的静态构造函数执行完毕后,才会继续分配实例内存、执行实例构造函数的逻辑。
你看到的ldarg.0是实例构造函数中引用当前实例的指令,但这个实例是在newobj指令执行时才分配内存的——静态构造函数的执行早于实例内存分配,和ldarg.0没有直接关联。
IL代码里找不到cctor调用的原因,是因为这部分逻辑属于CLR的运行时管控范畴,不是编译阶段生成的代码,而是CLR在处理类的首次访问(包括创建实例、访问静态成员等场景)时自动插入的执行逻辑。
内容的提问来源于stack exchange,提问作者Eric Su

