C#类型构造器

类型构造器也称为静态构造器,类构造器,或类型初始化器

类型构造器可以用于接口(C#不允许这样做),引用类型,值类型。实例构造器用来设置一个类型某个实例的初始化状态,类型构造器用来设置一个类型的初始化状态。默认情况下,类型没有定义类型构造器。下面展示如何定义值类型和引用类型的构造器:

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

可以发现一个特点是:无参,static标记,而且可访问性都是private,但是不能显示指定为private。当我们在值类型里面定义了一个类型构造器时,CLR不一定会调用这个静态构造器,如:

 internal struct SomeValType
 {
 static SomeValType()
 {
 Console.WriteLine("不会展示出来");
 }
 public Int32 m_x;
 }

 public class Program
 {
 static void Main()
 {
 SomeValType[] a = new SomeValType[10];
 a[0].m_x = 123;
 Console.WriteLine(a[0].m_x);//显示123

 }
 }

当JIT编译器编译一个方法时,它会检查在代码里面是否引入了其他类型。如果引入的的其他类型定义了类型构造器,则JIT会检测是否已经在AppDomain里面执行过。如果没有执行,则发起对类型构造器的调用,否则不调用。

在编译之后,线程会开始执行并最终获取调用构造器的代码。实际上有可能会是多个线程执行同一个方法,CLR想要确保一个类型构造器在一个AppDomain里面只执行一次,当一个类型构造器被调用时,调用的线程会获取一个互斥的线程同步锁,这时如果有其他的线程在调用,则会阻塞。当第一个线程执行完后离开,其他的线程被唤醒并发现构造器的代码执行过了,所以不会继续去执行了,从构造器方法返回。CLR通过这种方式来确保构造器仅仅被执行一次。

提示:

由于CLR会确保类型构造器在每一个AppDomain里面只会执行一次,是线程安全的。所以如果要初始化任何单例对象(singleton object),放在类型构造器里面是再合适不过了。

有这样一种情况,ClassA的类型构造器包含引用ClassB的代码,ClassB的类型构造器包含引用ClassA的代码,这种情况下CLR仍然会保证类型构造器只执行一次,但是应该尽量避免这种场景出现,因为这里应该避免调用类型构造器有了具体的顺序。

在类型构造器里面的代码仅仅访问的是类型的静态字段,通常的意图是初始化这些字段。C#提供了简单的语法来实现:

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

上面的代码在生成时,编译器自动会为SomeType创建一个类型构造器如下:

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

但是,C#不允许值类型使用内联字段初始化语法来实例化字段,所以下面这种方式就是错的:

internal sealed struct SomeType 
{ 
 private Int32 s_x = 5; //这样会报错,需要加static关键字
}

如果显示的定义了类型构造器,如下:

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

最终s_x的结果是10。这里,C#编译器首先会生成一个类型构造器方法,这个构造器首先初始化s_x为5,然后初始化为10。换句话说,在类型构造器里面的显示定义的代码会在实例化静态字段之后执行。

类型构造器性能

调用类型构造器并不那么简单,JIT编译器不得不决定是否生成调用它的代码,并且CLR要确保调用是线程安全的。当编译器决定发起一个调用来执行类型构造器,它必须判断是否应该这样做,有两种可能性:

1.JIT在创建类型的第一个实例的代码之前立即发起或者在访问类的非继承的字段,成员的代码之前立即调用

2.JIT在首次访问一个静态字段,静态方法,实例方法,或调用一个实例构造器的代码之前某个时间调用,因为CLR要确保静态构造器在其他成员被访问之前运行。

 

注   《CLR via C#》(Jeffrey Richter著)——.NET 界的经典之作,读的过程写点笔记跟大家分享,我也推荐大家看英文版,能够直接领会原意 

作者:张雪飞
出处:https://zhangxuefei.site/p/98
版权说明:欢迎转载,但必须注明出处,并在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

发表评论

电子邮件地址不会被公开。 必填项已用*标注