Skip to content

C# 类型构造器

🏷️ 《CLR via C#》

类型构造器 type constructor(静态构造器 static constructor)

类型构造器可应用于接口(虽然C#编译器不允许)、引用类型和值类型。
类型构造器的作用是设置类型的初始状态。
类型构造器总是私有的(C# 自动把它们标记为 private)。

cs
class SomeRefType
{
    static int MaxCount;
    static SomeRefType()
    {
        MaxCount = 100;
    }
}

类型构造器的调用

JIT 编译器在编译一个方法时,会查看代码中都引用了哪些类型。
任何一个类型定义了类型构造器,JIT 编译器都会检查针对当前 AppDomain,是否已经执行了这个类型构造器。
如果类型构造器从未执行,JIT 编译器会在它生成的本机(native)代码中添加对类型构造器的调用。
如果类型构造器已经执行,JIT 编译器就不添加对它的调用。

多个线程可能会同时执行到相同的类型构造器方法。
CLR 希望确保在每个 AppDomain 中,一个类型构造器只执行一次。
在调用类型构造器时,调用线程要获取一个互斥线程同步锁。

单个线程中的两个类型构造器包含相互引用的代码可能出问题。

如果类型构造器抛出未处理的异常,CLR 会认为类型不可用。试图访问该类型的任何字段或方法都会抛出 System.TypeInitializationException 异常。

类型构造器中的代码只能访问类型的静态字段,并且它的常规用途就是初始化这些字段。
C#提供了一种简单的语法来初始化类型的静态字段。

cs
class SomeRefType
{
    static int MaxCount = 50;
}

上面的代码相当于

cs
class SomeRefType
{
    static int MaxCount;
    static SomeRefType()
    {
        MaxCount = 50;
    }
}

如果在静态字段和类型构造器中都赋值的话,以哪个为准呢?

cs
class SomeRefType
{
    static int MaxCount = 50;
    static SomeRefType()
    {
        MaxCount = 100;
    }
}

查看生成的 IL 代码

cs
.class /*02000003*/ private auto ansi ConsoleApp1.SomeRefType
       extends [mscorlib/*23000001*/]System.Object/*01000010*/
{
  .field /*04000001*/ public static int32 MaxCount
  .method /*06000003*/ private hidebysig specialname rtspecialname static 
          void  .cctor() cil managed
  // SIG: 00 00 01
  {
    // 方法在 RVA 0x2065 处开始
    // 代码大小       16 (0x10)
    .maxstack  8
    IL_0000:  /* 1F   | 32               */ ldc.i4.s   50
    IL_0002:  /* 80   | (04)000001       */ stsfld     int32 ConsoleApp1.SomeRefType/*02000003*/::MaxCount /* 04000001 */
    IL_0007:  /* 00   |                  */ nop
    IL_0008:  /* 1F   | 64               */ ldc.i4.s   100
    IL_000a:  /* 80   | (04)000001       */ stsfld     int32 ConsoleApp1.SomeRefType/*02000003*/::MaxCount /* 04000001 */
    IL_000f:  /* 2A   |                  */ ret
  } // end of method SomeRefType::.cctor

  .method /*06000004*/ public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
  // SIG: 20 00 01
  {
    // 方法在 RVA 0x205c 处开始
    // 代码大小       8 (0x8)
    .maxstack  8
    IL_0000:  /* 02   |                  */ ldarg.0
    IL_0001:  /* 28   | (0A)000011       */ call       instance void [mscorlib/*23000001*/]System.Object/*01000010*/::.ctor() /* 0A000011 */
    IL_0006:  /* 00   |                  */ nop
    IL_0007:  /* 2A   |                  */ ret
  } // end of method SomeRefType::.ctor

} // end of class ConsoleApp1.SomeRefType

可以看到只生成了一个 cctor (类型构造器)方法,先将 MaxCount 赋值为 50,然后再赋值为 100。