我通读了乔恩·斯凯特(Jon Skeet)关于beforefieldinitarticle的信息,偶然发现了一个问题。他提到,可以在调用静态字段的第一个引用之前的任何时间调用类型初始值设定项。

这是我的测试代码:

class Test1
{
    public static string x1 = EchoAndReturn1("Init x1");

    public static string EchoAndReturn1(string s)
    {
        Console.WriteLine(s);
        return s;
    }
}

class Programm
{
    public static void Main()
    {
        Console.WriteLine("Starting Main");

        Test1.EchoAndReturn1("Echo 1");

        Console.WriteLine("After echo");

        string y = Test1.x1;  //marked line
    }
}

输出为:
Init x1
Starting Main
Echo 1
After echo

但是没有标记行,因此没有调用静态字段x1,输出为:
Starting Main
Init x1
Echo 1
After echo

因此,使用beforefieldinit标记的对象的调用会影响其类型初始值设定项的调用吗?还是这属于他提到的beforefieldinit的奇怪效果?

最佳答案

我不确定在这里问什么问题。也许如果我解释一下抖动的原因,那将回答这个问题。

具有显式静态构造函数的静态类对cctor的运行时间具有“严格”的语义:cctor在首次使用该类型的成员之前立即运行。所以如果你有

if (whatever) x = Foo.Bar;

如果Foo为false,则whatever的cctor不会运行,因为我们尚未实际使用过成员。

考虑一下这对于固定代码意味着什么。 您如何为具有要求的语言编写抖动?

对于静态方法调用,您可以在每个调用站点放置一些前传,以检查cctor是否已运行。但这会使每个调用站点变大和变慢。

您可以将前传放入静态方法本身。这样可以使调用站点变小,但是每个调用仍然会稍微变慢。

或者,您可能会很聪明,并在第一次静态方法被静态化时将检查放入抖动中。这样一来,您只需支付一次检查费用,并且调用站点保持较小。即时成本增加了,但只是很小的一部分。 jitting已经很昂贵了。

但是请注意,这样做会排除可能导致方法在首次调用之前被抖动的任何优化,因为这种优化现在会带来正确性问题。优化几乎总是需要权衡的!

但是对于现场访问,没有方法可以进行jit。每次进入该领域(可能是第一个)之前,都必须在抖动之前加上一些前传。因此,访问字段不仅会变慢,而且代码也会变大。

您可能会想为什么不将字段设置为属性并将前传放在getter和setter的拼合上呢?但这不起作用,因为字段是变量,而属性不是。例如,我们需要能够通过refout传递静态字段,但是您不能使用属性来实现。该字段可能是volatile,并且不能是属性。等等。

能够避免这些在现场访问上的开销将是很好的。

没有显式cctor但具有编译器生成的隐式cctor来初始化静态字段的静态类将获得“松弛”语义,其中抖动仅保证在访问字段之前的某个时刻调用cctor。您的程序使用这些宽松的语义。

在第一个版本中,使用字段访问时,抖动从对方法的分析中知道可以访问静态字段。 (为什么“可能”?像以前一样,访问权限可能在if下。)在第一次访问之前的任何时候都允许抖动运行cctor,所以它做的是在Main抖动时记下的字样。 ,请检查是否已运行Test1 cctor,如果尚未运行,请运行

如果Main被第二次调用,嘿,它只会被打一次。同样,支票的费用仅在首次致电时承担。 (当然,在大多数程序中,Main只会被调用一次,但是如果您喜欢这种事情,则可以编写一个递归的Main。)

在您的第二个程序中,没有字段访问权限。抖动还可能导致访问静态方法,并且cctor可以在jit时针对Main运行。它不是。为什么不?我不知道;您必须向抖动小组询问一下。但是关键是,抖动完全具有使用启发式方法来决定是否在jit时运行cctor的权利,并且这样做。

抖动还具有使用启发式方法来决定是否调用不涉及任何字段的静态方法的权限的权利。在这种情况下,显然这样做是不必要的。

您的问题似乎是“这些启发式是什么?”答案是...好吧,我还不确定答案是什么,它是运行时的实现细节,随时可能改变。您已经在此答案中看到了关于这些启发式方法的本质的一些很好的猜测:
  • 检查是否将T的任何静态方法设为jitted时,T的cctor是否需要运行
  • 检查访问T的静态字段的任何方法是否为jitted时,是否需要运行T的cctor?

  • 这些试探法将满足宽松语义的要求,并避免在调用站点发出所有检查,并且仍将确保合理的行为。

    但是您不能依靠这些猜测。您可以依靠的是,cctor将在第一次进行现场访问之前的某个时间运行,这就是您所要获得的。特定方法中是否存在现场访问显然是该启发式方法的一部分,但是这些启发式方法可能会发生变化。

    10-06 07:09