Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/278.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/symfony/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#中静态类初始化的顺序是确定的吗?_C#_Static_Deterministic - Fatal编程技术网

C#中静态类初始化的顺序是确定的吗?

C#中静态类初始化的顺序是确定的吗?,c#,static,deterministic,C#,Static,Deterministic,我已经做了一些搜索,我认为以下代码可以保证生成输出: B.X = 7 B.X = 0 A.X = 1 A = 1, B = 0 我已经运行了很多次,并且总是在代码部分上面得到输出;我只是想确认它会改变吗?即使在文本上,类A和类B被重新排列 是否保证第一次使用静态对象将触发其静态成员的初始化,然后实例化其静态构造函数?对于该程序,在main中使用A.X将触发A.X的初始化,后者依次初始化B.X,然后B(),完成A.X的初始化后,将进入A()。最后,Main()。。。但它不是“文本顺序”。此

我已经做了一些搜索,我认为以下代码可以保证生成输出:

B.X = 7

B.X = 0

A.X = 1

A = 1, B = 0
我已经运行了很多次,并且总是在代码部分上面得到输出;我只是想确认它会改变吗?即使在文本上,类
A
和类
B
被重新排列


是否保证第一次使用静态对象将触发其静态成员的初始化,然后实例化其静态构造函数?对于该程序,在main中使用
A.X
将触发
A.X
的初始化,后者依次初始化
B.X
,然后
B()
,完成
A.X
的初始化后,将进入
A()
。最后,
Main()。。。但它不是“文本顺序”。此外,它可能不会以完全惰性的方式执行(这意味着只有在第一次引用静态变量时)。然而,在您使用整数的示例中,这并没有什么区别


在某些情况下,需要惰性初始化(特别是昂贵的单例),在这种情况下,您有时必须正确地进行初始化。

静态成员的确定性初始化确实是有保证的。。。但它不是“文本顺序”。此外,它可能不会以完全惰性的方式执行(这意味着只有在第一次引用静态变量时)。然而,在您使用整数的示例中,这并没有什么区别


在某些情况下,需要进行延迟初始化(特别是使用昂贵的单例),在这种情况下,您有时必须正确地进行初始化。

在C#规范中,有四种不同的规则参与了这一保证,这是针对C#的。NET运行时所做的唯一保证是在使用类型之前开始类型初始化

  • 在类型初始值设定项运行之前,静态字段是零初始化的
  • 静态字段初始值设定项紧跟在静态构造函数之前运行
  • 在第一个实例构造函数调用或第一个静态成员引用时调用静态构造函数
  • 函数参数按从左到右的顺序求值
依赖这一点是一个非常糟糕的想法,因为它可能会让任何阅读您的代码的人感到困惑,特别是如果他们熟悉语法类似的语言,而这些语言不能满足上述四个条件


请注意,Porges的评论与我最初的声明(基于.NET行为)有关,即保证太弱,无法保证观察到的行为。Porges说保证足够有力是正确的,但事实上涉及的链条远比他所说的复杂。

关于C规范中的四个不同规则涉及到了这一保证,这是针对C的。NET运行时所做的唯一保证是在使用类型之前开始类型初始化

  • 在类型初始值设定项运行之前,静态字段是零初始化的
  • 静态字段初始值设定项紧跟在静态构造函数之前运行
  • 在第一个实例构造函数调用或第一个静态成员引用时调用静态构造函数
  • 函数参数按从左到右的顺序求值
依赖这一点是一个非常糟糕的想法,因为它可能会让任何阅读您的代码的人感到困惑,特别是如果他们熟悉语法类似的语言,而这些语言不能满足上述四个条件


请注意,Porges的评论与我最初的声明(基于.NET行为)有关,即保证太弱,无法保证观察到的行为。Porges认为担保足够有力是正确的,但事实上,担保所涉及的链条远比他建议的复杂。

直接来自ECMA-334:

17.4.5.1:“如果类中存在静态构造函数(§17.11),则在执行该静态构造函数之前立即执行静态字段初始值设定项。否则,静态字段初始值设定项将在该类静态字段首次使用之前的依赖于实现的时间执行。”

以及:

17.11:静态构造函数的执行由第一个 在应用程序域中发生的以下事件之一:

  • 将创建该类的一个实例
  • 将引用该类的任何静态成员
如果类包含开始执行的主方法(§10.1),则该类的静态构造函数 在调用Main方法之前执行。如果类包含任何带有初始值设定项的静态字段,则 初始值设定项在执行静态构造函数之前立即按文本顺序执行(§17.4.5)

因此,顺序是:

  • 使用了
    A.X
    ,因此调用了
    static A()
  • A.X
    需要初始化,但它使用
    B.X
    ,因此调用了
    static B()
  • B.X
    需要初始化,并且初始化为7<代码>B.X=7
  • B
    的所有静态字段都已初始化,因此调用
    static B()
    X
    被打印(“7”),然后它被设置为
    A.X
    A
    已经开始初始化,因此我们得到了
    A.X
    的值,这是默认值(“初始化类时,该类中的所有静态字段首先初始化为其默认值”)<代码>B.X=0
    ,并打印(“0”)
  • 初始化
    B
    ,将
    A.X
    的值设置为
    B.X+1
    <代码>A.X=1
  • A
    的所有静态字段都是
    static class B
    {
        public static int X = 7;
        static B() {
            Console.WriteLine("B.X = " + X);
            X = A.X;
            Console.WriteLine("B.X = " + X);
        }
    }
    
    static class A
    {
        public static int X = B.X + 1;
        static A() {
            Console.WriteLine("A.X = " + X);
        }
    }
    
    static class Program
    {
        static void Main() {
            Console.WriteLine("A = {0}, B = {1}", A.X, B.X);
        }
    }
    
    private static int b = Foo();
    private static int a = 4;
    
    private static int Foo()
    {
        Console.WriteLine("{0} - Default initialization", a);
        a = 3;
        Console.WriteLine("{0} - Assignment", a);
        return 0;
    }
    
    public static void Main()
    {
        Console.WriteLine("{0} - Variable initialization", a);
    }