【问题标题】:Nested static classes initialized in wrong order?嵌套的静态类以错误的顺序初始化?
【发布时间】:2015-07-27 17:27:39
【问题描述】:

嵌套静态类在其父级之前初始化似乎存在问题。请参阅下面的示例,我已经包含 cmets 来指示我期望的东西被初始化/调用的顺序,以及实际的顺序。在这种情况下,我调用Test1.Test2.GetName(),所以我希望静态类按以下顺序初始化:Test1Test2

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();
            }
        }
    }
}

我认为静态是在第一次接触类时初始化的,但显然接触嵌套类并没有首先初始化父类!这对我来说就像一个错误。它正在这样做:

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

所以虽然Test2.Name是入口点,但理论上它并没有在需要的时候初始化。

【问题讨论】:

  • the literature 中得到了很好的覆盖。引用:“用户无法控制何时在程序中执行静态构造函数”。换句话说,顺序不是“错误的”,而是未定义的。
  • 它还说“在创建第一个实例或引用任何静态成员之前自动调用”这在这种情况下是不正确的,因为嵌套的静态类首先被初始化!我不希望有控制权,但我应该能够假设这会发生的顺序。
  • 您应该改用初始化方法。这样你就可以控制初始化。在最坏的情况下,您可以从 Program 中的静态构造函数初始化它们。
  • 这是我作为解决方法所做的,但这仍然感觉像是编译器中的一个错误。

标签: c# .net debugging


【解决方案1】:

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

问题在于嵌套类不是其父类的静态成员。这完全是一个不同的班级。它有一个 nested attribute 但这仅用于成员可见性目的。

来自ECMA-335(CLI 规范):

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

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

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

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

这里,意思一清二楚。

但这不会使您的示例有效,因为它具有循环依赖:

  • Test1静态构造函数调用Test2.GetName(),表示会触发Test2的初始化
  • Test2静态构造函数(隐式)调用Test1.Name.ToString() + "_Test2",会触发Test1的初始化

不管怎样,它都行不通,你必须修复那个代码。

【讨论】:

  • 明白。虽然我仍然建议嵌套类应该在它们的父类之后初始化,尽管这种关系基本上没有意义。如果可以选择顺序,则应该是从外到内。在我的情况下,它只是几个嵌套的静态类保存设置,所以我可以做类似Settings.UI.Something.SomeValue 的事情——这里没有做任何严肃的工作,所以这是一个有争议的问题。我只是将父类设置为在其静态构造函数中初始化子类并称之为一天。谢谢!
猜你喜欢
  • 2010-12-02
  • 1970-01-01
  • 2018-12-19
  • 1970-01-01
  • 2020-01-31
  • 1970-01-01
  • 1970-01-01
  • 2021-07-12
相关资源
最近更新 更多