初始化顺序错误的嵌套静态类

本文关键字:嵌套 静态类 错误 顺序 初始化 | 更新日期: 2023-09-27 18:04:51

嵌套静态类在其父类之前初始化似乎存在问题。请参阅下面的示例,我包含了注释来指示我期望初始化/调用的顺序,以及实际的顺序。在本例中,我调用Test1.Test2.GetName(),因此我希望静态类按照以下顺序初始化:Test1, Test2

using System;
using System.Collections.Generic;
namespace ConsoleApplication1 {
    class Program {
        static void Main(string[] args) {
            Console.WriteLine(Test1.Test2.GetName());
            Console.ReadKey();
        }
    }
    public static class Test1 {
        // Actual Order: 2; Expected: 1
        private static string Name = "Test1";
        public static List<string> Names { get; private set; }
        // Actual Order: 3; Expected: 2
        static Test1() {
            Test1.Names = new List<string>(new string[] {
                Test2.GetName()
            });
        }
        public static class Test2 {
            // Actual Order: 1; Expected: 3
            private static string Name = Test1.Name.ToString() + "_Test2";
            // Actual Order: 4; Expected: 4
            public static string GetName() {
                return Name.ToString();
            }
        }
    }
}

我认为静态是初始化的第一次类被触摸,但显然触摸嵌套类不会首先初始化父类!我觉得这是个bug。它的作用是:

Test2.Name -> Test1.Name -> Test1() -> Test2.GetName() -> Test2.Name

因此,尽管Test2.Name是入口点,但理论上,在需要它的时候,它并没有被初始化。

初始化顺序错误的嵌套静态类

在创建第一个实例或引用静态成员之前,自动调用静态构造函数初始化类。

问题是嵌套类不是父类的静态成员。这是一个完全不同的班级。它有一个nested属性,但这只是为了成员可见性的目的。

来自ECMA-335 (CLI规范):

I.8.11.5嵌套类型定义
嵌套类型定义与顶级类型定义相同,但有一个例外:顶级类型定义类型具有可见性属性,而嵌套类型的可见性与的可见性相同封闭类型。

因此,行为是正确的。文档不要求在调用嵌套类时初始化父类。无论如何,您应该注意应该避免嵌套的公共类,就像公共字段一样,因为这不是良好的封装。嵌套类应该主要用于实现细节,并对外部隐藏。

如果一个嵌套类需要先初始化它的父类,你可以在它的静态构造函数中显式地强制它:

static Test2()
{
    RuntimeHelpers.RunClassConstructor(typeof(Test1).TypeHandle);
}

这里的意思非常清楚。

但这不会使你的例子有效,因为它有一个循环依赖:

  • Test1静态构造函数调用Test2.GetName(),这意味着它将触发Test2的初始化
  • Test2静态构造函数(隐式)调用Test1.Name.ToString() + "_Test2",这将触发Test1的初始化

无论哪种方式,它都不能工作,你必须修复代码