记得我在分析readonly关键字那篇文字里,我用到了类型构造器。后来想了想,我能不能不用类型构造器呢?左想右想,还是不行,非得用它不可,因为字段是readonly修饰,且他娘的还是static的,地球人都知道,我绝不能拿对象构造器去初始化一个static修饰的字段。
我查了查CLR规范,发现类型构造器非常棘手,不好用,甚至值类型不能定义类型构造器。
废话少说,来看DEMO。
先定义个带类型构造器的值类型。
namespace static类型构造器
{
struct StataicBuilderValueType
{
static StataicBuilderValueType()//never do here
{
Console.WriteLine("hai......");
Console.ReadLine();
}
}
}
再在主类中构造十个StataicBuilderValueType类型的对象,看DEMO。
namespace static类型构造器
{
class Program
{
static void Main (string[] args)
{
StataicBuilderValueType[] sv=new StataicBuilderValueType[10];
}
}
}
我在类型构造器中设置一个断点,然后跟踪,发现程序在断点处根本就不停,哗,全过去了。我做了N次。
所以切记,不要在结构,值类型中,使用类型构造初试化值,它会让你失望的。
再看看CLR对类型构造器的其他约束。
在CLR规范中这样写道;“CLR保证每个应用程序域的类型构造器只执行一次,而且线程是安全的”,这是原话。
解释一下,在CLR规范中,应用程序域的定义,她是程序进程的逻辑叫法.
所谓,线程安全,就是资源在同一时间点,只有一个线程能访问,其他线程要访问,必须等上一个线程,释放她的线程同步锁。
来看DEMO
定义带类型构造器的引用类型
namespace static类型构造器
{
class Test2
{
static int i = 0;
static Test2()
{
i = i+1;
Console.WriteLine("hai......");
}
public static Int32 getValue()
{
return i;
}
}
}
再定义带MAIN方法的主类
namespace static类型构造器
{
class Program
{
static void Main (string[] args)
{
int i = Test2.getValue();
Console.WriteLine(i);
System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(ThreadProc));
thread.Start();
Console.ReadLine();
}
public static void ThreadProc()
{
int n=Test2.getValue();
Console.WriteLine(n);
}
}
}
在Main方法中首先调用类型构造器,因为执行了Test2.getValue(),对类型进行了引用,当然会执行类型构造器了。然后,在主线程中开辟一新的线程thread,在线程thread中也执行Test2.getValue()方法。
那么在thread中会不会对引用类型的构造器进行调用呢?
来看输出
可以看到当主线程调用类型构造器,并释放线程同步锁后,线程thread,虽然显示的去调用类型构造器,但是底层CLR根本没执行,I还是等于1。这是因为CLR会去检查是否已经执行了类型构造器,如果执行了,CLR永远不再生成调用类型构造器的本地代码。
备注;ctor与cctor的区别