CLR via C#-类型构造器

类型构造器

除了实例构造器,CLR还支持类型构造器,也称为静态构造器、类构造器或者类型初始化构造器。类型构造器可应用于引用类型和值类型。

实例构造器的作用是设置类型的实例的初始状态。对应地,类型构造器的作用是设置类型的初始状态。


类型默认没有类型构造器

如果定义也只能定义一个,此外类型构造器永远没有参数,以下代码展示了如何在C#中为引用类型和值类型定义一个类型构造器。

internal sealed class SomeRefType{
    static SomeRefType(){}
}
internal struct SomeValType{
    static SomeValType(){}
}

定义类型构造器类似于定义无参实例构造器,区别在于必须标记为static。

类型构造器是私有的

C#把他们自动标记为private,如果在源代码中显式将类型构造器标记为private或者其他访问修饰符,C#编译器会报错。

之所以必须私有,是为了防止任由开发人员写的代码调用它,对他的调用总是由CLR负责。

 


类型构造器只执行一次

类型构造器的调用比较麻烦。JIT编译器在编译一个方法时,会查看代码中都引用了哪些类型。

任何一个类型定义了类型构造器,JIT编译器都会检查针对当前AppDomain,是否已经执行了这个类型构造器。

如果构造器从未执行,JIT编译器会在他生成的本机native代码中添加对类型构造器的调用。

如果类型构造器已经执行,JIT编译器就不添加对他的调用,因为他知道类型已经初始化好了。

由于CLR保证一个类型构造器在每个AppDomain中只执行一次,而且这种执行是线程安全的,

所以非常适合在类型构造器中初始化类型需要的任何单实例对象Singleton。

不要在两类型的类型构造器中写相互引用的代码

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

假定A的类型构造器包含了引用sB的代码,B的类型构造器包含了A的代码。

在这种情况下,CLR仍然保证每个类型构造器的代码只被执行一次。

但是完全有可能在A的类型构造器还没有完全执行完毕的前提下就开始执行B的类型构造器。

不要在值类型中定义类型构造器

虽然能在值类型中定义类型构造器。但永远不要真的这么做,因为CLR有时不会调用值类型的静态构造器。


使用内联字段初始化语法静态字段

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

internal sealed class SomeType{
    private static Int32 s_x=5;    
}

值类型允许使用内联字段初始化静态字段

虽然C#不允许值类型为他的实例字段使用内联字段初始化语法,但可以为静态字段使用。换句话说,将前面定义的SomeType类型从class改为struct,那么代码也能通过编译。

生成上述代码时,编译器自动为SomeType生成一个类型构造器,就好像源代码本来是这样写的。

internal sealed class SomeType{
    private static Int32 s_x;
    static SomeType(){ s_x = 5 }
}

类型构造器不应调用基类型的类型构造器

这种调用之所以没必要,是因为类型不可能有静态字段是从基类型分享或继承的。

 


初始化字段的顺序

internal sealed class SomeRefType{
    private static Int32 s_x = 5;
    static SomeType(){
        s_x = 10;
    }
}

在这个例子中,C#编译器只生产一个类型构造器方法,他首先将s_x初始化为5,再把它修改成10.

换言之,当C#编译器为类型构造器生成IL代码时,他首先生成的是初始化静态字段所需的代码,然后才会添加你的类型构造器方法中显式包含的代码。

原文地址:https://www.cnblogs.com/errornull/p/9769433.html