static constructors和BeforeFieldInit?

Roy*_*mir 5 .net c# .net-4.0

如果类型没有静态构造函数,则字段初始值设定项将在使用的类型之前执行 - 或者在运行时的奇思妙想之前的任何时间执行

为什么这段代码:

void Main()
{ 
  "-------start-------".Dump();
   Test.EchoAndReturn("Hello");
  "-------end-------".Dump();

}

 class Test
{
    public static string x = EchoAndReturn ("a");
    public static string y = EchoAndReturn ("b");
    public static string EchoAndReturn (string s)
    {
        Console.WriteLine (s);
        return s;
    }
}
Run Code Online (Sandbox Code Playgroud)

产量:

-------start-------
a
b
Hello
-------end-------
Run Code Online (Sandbox Code Playgroud)

而这段代码:

void Main()
{ 
  "-------start-------".Dump();
   var test=Test.x;
  "-------end-------".Dump();

}
Run Code Online (Sandbox Code Playgroud)

产量

a
b
-------start-------
-------end-------
Run Code Online (Sandbox Code Playgroud)

顺序ab理解.但为什么与处理 static method不同的static field.

我的意思是为什么起始行和结束行在静态方法与静态字段的不同位置?我的意思是 - 在这两种情况下他都要初始化那些领域......为什么呢?

(我知道我可以添加静态ctor,使其相同 - 但我问这个特殊情况.)

(ps Dump()就像console.write)

Mar*_*ell 6

释放JIT的行为(从4.0 IIRC)不运行静态初始化程序,除非您调用的方法触及静态字段.这可能意味着静态字段初始化.如果我在调试器之外的发行版中运行你的第一个代码,我得到:

-------start-------
Hello
-------end-------
Run Code Online (Sandbox Code Playgroud)

如果我使用调试器附加(发布)或调试版本(附带或不附带调试器)运行它,我得到:

-------start-------
a
b
Hello
-------end-------
Run Code Online (Sandbox Code Playgroud)

到目前为止很有趣.为什么你得到:

a
b
-------start-------
-------end-------
Run Code Online (Sandbox Code Playgroud)

看起来每个方法的JIT基本上负责在这种情况下运行静态构造函数.你可以通过添加:

if(NeverTrue()) { // method that returns false
        "-------start-------".Dump();
        var test = Test.x;
        "-------end-------".Dump();
}
Run Code Online (Sandbox Code Playgroud)

将打印(即使在没有调试器的情况下发布)

a
b
Run Code Online (Sandbox Code Playgroud)

所以访问字段的可能性是关键.如果我们改为Test.x调用一个不访问字段的方法(并删除那个NeverTrue()东西),那么我们就不会得到任何输出.

因此:在CLI的某些版本中,静态初始化程序的执行可能会延迟到包含对任何字段的提及的方法的JIT步骤(它不会检查该字段是否具有初始化程序).

我们甚至可以在不运行静态初始化程序的情况下创建对象实例,只要我们不接触静态字段:

 public Test()
 {
     a = "";
 }
 string a;
Run Code Online (Sandbox Code Playgroud)

有:

"-------start-------".Dump();
new Test();
"-------end-------".Dump();
Run Code Online (Sandbox Code Playgroud)

打印只是(发布,没有调试器):

-------start-------
-------end-------
Run Code Online (Sandbox Code Playgroud)

然而!我们不应该根据这个时间构建任何东西:

  • 它在.NET版本之间变化
  • 它可能会在平台(x86,x64,CF,SL,.NETCore等)之间发生变化
  • 它可以根据是否附加调试器以及它是否是调试/发布版本而更改