我有个类Class1,代码如下:
class Class1
{
public static string str = "fsdafdsa";
public static readonly Class1 anobject = new Class1();
static Class1()
{
System.Console.WriteLine("静态构造函数调用");
}
private Class1()
{
}
public static Class1 c()
{
return new Class1();
}
}
在主函数里面的调用代码:
static void Main(string[] args)
{
string s = Class1.str;
Class1 temp =Class1.c();
Console.WriteLine("程序开始");
Console.ReadLine();
}
当我一步步调试程序的时候发现,当我把Class1中的静态构造函数去掉后,程序会先初始化Class1中的静态成员,然后再去调用Main函数。但当我在Class1中加上静态构造函数后,程序就会先执行主函数,当在主函数中调用Cass1的时候,再去初始化Class1中的成员。后来我又发现,当我去掉Main函数中的代码“string s = Class1.str;”后,无论Class中有无静态构造函数,程序都会先执行Main函数。请问各位大侠,这是为什么?
这是编译器优化的问题,如果你在类外部访问类的公共静态字段,那么这个字段必须在访问前就已经被初始化好,如果你没有静态构造函数,编译器不知道什么时候去初始化这个静态字段,所以编译器只能在主函数之前去初始化这些静态变量。但如果你在类的内部访问类的静态成员,由于类的静态字段在类的公共方法或属性可以执行之前肯定已经初始化好了,所以不需要在主函数前初始化静态字段。
那么你要问了,编译器编译出的代码为什么不能在调用 Class1.str之前先检查一下这个类的静态字段有没有初始化过,如果没初始化,就先初始化。当然是可以的,但这样势必影响静态字段的访问效率,因为每次调用静态字段都要判断是否已经初始化过了。所以编译器出于优化的考虑做了这样的设计。
下面看汇编
有静态构造函数情况下
string s = Class1.str;
00000029 mov edx,25h
0000002e mov ecx,9896BCh
00000033 call FFCA1218
00000038 mov eax,dword ptr ds:[022F98B0h]
0000003d mov edi,eax
在第一次获取Class1.str 前 有一个call FFCA1218 这就是调用静态构造函数。
没有静态构造函数
string s = Class1.str;
00000029 mov eax,dword ptr ds:[022F98B0h]
0000002e mov edi,eax
这时候直接就赋值了,那么只能在主函数进入前初始化静态字段了。
没有静态构造函数,但没有在类以外调用静态字段。
Class1.c();
00000025 call FFCBB410
0000002a nop
直接远程调用 c 方法,但在调用这个方法前程序跳到了初始化静态字段的地方,应该是有个地方去判断类还没有初始化完成,于是先初始化了。编译器优化时应该是认为远程调用方法因为本身就比较耗时,加入一个判断语句过对性能的影响不会很大。
如果类中包含用来开始执行的 Main 方法,则该类的静态构造函数将在调用 Main 方法之前执行。
任何带有初始值设定项的静态字段,则在执行该类的静态构造函数时,先要按照文本顺序执行那些初始值设定项。
如果没有编写静态构造函数,而这时类中包含带有初始值设定的静态字段,那么编译器会自动生成默认的静态构造函数。
Main是程序执行的入口,执行调用Class1时(在执行 string s = Class1.str是)先执行Class1的类静态变量赋值,然后执行类的构造函数。