我担心以下两种行为之间的相互作用:
http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-335.pdf#page=179
2.1。如果类型尚未初始化,请尝试获取初始化锁。
2.2.1。如果未成功,请查看该线程还是等待该线程完成的任何线程已经持有该锁。
2.2.2。如果是这样,则返回,因为阻塞将导致死锁。现在,该线程将看到该类型的未完全初始化状态,但是不会出现死锁。
http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-335.pdf#page=69
如果标记为BeforeFieldInit,则在首次访问为该类型定义的任何静态字段时或之前执行该类型的初始化方法。
此代码示例演示了可能的交互:
static class Foo {
public static int A = 1;
}
static class Bar {
public static int B = Foo.A;
}
static class Program {
static void Main() {
Console.WriteLine(Bar.B);
}
}
在任何理智的环境中进行测试时,它将输出1。但是,似乎规范允许通过执行以下操作来输出0:
这真的允许吗?我应该如何编写类型初始值设定项,以免被它咬住?
答案 0 :(得分:3)
这真的允许吗?
肯定看起来像是规范所允许的。
在任何理智的环境中进行测试时,它将输出1。
是的。了解优化背后的原因是有帮助的。 “松弛语义”的目的是移动对“静态构造函数是否运行?”的检查。从访问类型的执行时间到访问类型的方法的
void M()
{
blah
if blah blah
... Foo.A ...
if blah blah blah
... Foo.A ...
blah blah blah
}
假设现在是M
的准时,并且Foo
的cctor尚未执行。为了严格遵守,抖动必须在每次访问Foo.A 时生成代码,以检查Foo
cctor是否已经执行,如果尚未执行,则执行该代码。
但是,如果我们在准时执行cctor调用 ,则 jitter 就会知道Foo
是在M
内部访问的,因此可以调用当M
被抖动时,返回cctor,然后跳过在M
内部生成每个检查。
在jit时执行cctor时,抖动足够聪明,可以执行正确的操作;它不会按照您所描述的那样以“错误”的顺序执行cctor,因为编写抖动的人是理智的人,他们只是想使您的代码更快。
我应该如何编写类型初始值设定项,以免被它咬住?
您应该假设符合实现的作者是理智的。
如果出于某种原因您不能假设:您可以将所有您关心的静态字段初始化器放入静态构造函数中。 C#编译器不允许在具有静态构造函数的类型上使用BeforeFieldInit
语义。