gpt4 book ai didi

.net - 循环类型初始值设定项

转载 作者:行者123 更新时间:2023-12-03 23:58:32 25 4
gpt4 key购买 nike

我正在设计一个特定于域的 CLI,在过去的几周里,我一直在调查各种极端情况,以确保我对所需的内容有深刻的了解。

现在我正在研究类型构造。我正在考虑以下场景,我找不到太多信息:

class C
{
public static int Field = D.Field;
}

class D
{
public static int Field = C.Field;
}

class TestProg
{
static void Main()
{
Console.WriteLine( D.Field );
}
}

这两个类都标有 beforefieldinit .

有趣的是,这个程序实际上编译并在 MSCLR 上运行,产生:
0

所以看起来在实践中发生的事情是 C..cctor 中的触发点。构建 D被忽略,因为 D的 build 已经开始。然而,对我来说,这个程序看起来无效,因为 C..cctor在完全构建之前使用某物。

许多人会指出上述场景毫无意义,但作为 CLI 的实现者,我很担心,因为我需要知道在类型初始值设定项中的循环引用方面我有多少自由度。

我在 ECMA-335 中可以找到的关于此的所有内容是:

If marked BeforeFieldInit then the type’s initializer method is executed at, or sometime before, first access to any static field defined for that type.



在这种情况下,“执行于”这个词留下了一些歧义,因为它们没有指定是否必须执行整个初始化程序或是否必须简单地开始执行。

我遇到了一条评论,暗指 CLI 规范中关于循环引用案例的特定规则,但到目前为止,我在 ECMA-335 中根本找不到任何提及该问题的内容。

所以,我的问题是:
  • 上面的程序是否依赖于未定义的行为?还是未指明的行为?
  • 如果我的 CLR 拒绝加载上述程序,它是否仍然符合要求?
  • 如果不是,关于类型构造函数中循环引用的确切规则是什么?
  • 是否有任何有效的、有用的设计模式可以在流程控制打折时导致程序类型初始值设定项的有向图中出现循环?
  • 最佳答案

    这是一个答案:

    II.10.5.3.1 - ECMA 335 的 II.10.5.3.3 相当明确地回答了这个问题,并解释了以下大部分行为。

    这是一个非答案,但对于评论来说远远不够。

    这在我看来是说明性的:

    namespace ConsoleApplication1
    {
    class Program
    {
    static void Main(string[] args)
    {
    Console.WriteLine(D.Field);
    Console.WriteLine(C.Field);
    Console.WriteLine(C.FieldX);
    Console.WriteLine(D.FieldY);
    Console.ReadLine();
    }
    }
    }
    class D
    {
    public static int Field = C.FieldX;
    public static int FieldY = C.Field;
    }


    class C
    {
    public static int FieldX = 5;
    public static int Field = D.Field;
    }

    哪个可靠地输出:
    5
    0
    5
    0

    具有引用类型的类似结果。

    我相信你的'所以似乎在实践中发生的事情是 C..cctor 中构造 D 的触发点被忽略,因为 D 的构造已经开始。是假的。的实际意图
    '如果标记为 BeforeFieldInit 则该类型的初始化方法在第一次访问为该类型定义的任何静态字段时或之前的某个时间执行。
    似乎对我来说,实际上(在这个例子中)实际上是:

    CLR 识别 Main 使用 D,想要初始化 D,识别 D 使用 C,想要初始化 C 并且在 D 的初始化真正开始之前执行。 (见 II.10.5.3.3)

    即 C 在 D 之前严格初始化,任何对 D 的引用都将为空/默认。

    在我的示例中,交换前两条主线
        static void Main(string[] args)
    {
    Console.WriteLine(C.Field);
    Console.WriteLine(D.Field);

    并且 D 将首先严格初始化,输出

    0
    0
    5
    0

    现在我的问题是你真正的问题是什么!即你有一个更接近你“关心”的循环依赖的例子。

    还要注意这里的行为完全类似于
    namespace ConsoleApplication1
    {
    class Program
    {
    static void Main(string[] args)
    {
    Console.WriteLine(D.Field);
    Console.WriteLine(D.FieldY);
    Console.ReadLine();
    }
    }
    }
    class D
    {
    public static int Field = FieldY;
    public static int FieldY = 5;
    }

    哪个是 0 5

    我觉得这是“相同”的例子,但在单一类型的初始化中,即你可以依赖字段的“存在”而不是初始化,如果你想要更多的确定性排序然后编写静态构造函数(IIRC 在fieldinit 之前停止)并创建II.10.5.3.3 中更具确定性的方法。

    关于.net - 循环类型初始值设定项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20062098/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com