【问题标题】:Static constructor not called before static fields在静态字段之前未调用静态构造函数
【发布时间】:2014-02-25 21:32:14
【问题描述】:

我有一堂课如下:

static class Configuration
    {
        private static AppSettingsSection _appSettingsLogsSection;
        static Configuration()
        {
            var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
            _appSettingsLogsSection = config.GetSectionGroup("Logs").Sections["appSettings"] as AppSettingsSection;
        }

        public static int LogSendIntervalMinutes = Convert.ToInt32(_appSettingsLogsSection.Settings["LogSendIntervalMinutes"]);

    }

现在,根据我的理解,应该在第一次引用任何静态成员之前调用静态构造函数。但令人惊讶的是,它的行为并非如此。当我从 Main 类引用 LogSendIntervalMinutes 时,不是触发静态构造函数,而是直接调用静态字段,导致 NullReferenceException。

我在这里做错了什么吗?我的理解是否正确?

【问题讨论】:

  • 使用ILSpyReflector查看编译代码你会找到答案。实际上,字段初始值设定项会在构造函数的实际代码之前自动移动到构造函数中。 (静态移动到静态一和实例到实例构造器)。
  • 我已经为你的班级添加了一个快照,使用反射器供你参考。

标签: c# .net static clr


【解决方案1】:

静态字段总是在调用静态构造函数之前初始化。您也应该在静态构造函数中初始化LogSendIntervalMinutes。我建议您甚至将其设为属性:

static class Configuration
{
    private static AppSettingsSection _appSettingsLogsSection;
    public static int LogSendIntervalMinutes { get; private set; }
    static Configuration()
    {
        var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
        _appSettingsLogsSection = config.GetSectionGroup("Logs").Sections["appSettings"] as AppSettingsSection;
        LogSendIntervalMinutes = Convert.ToInt32(_appSettingsLogsSection.Settings["LogSendIntervalMinutes"]);
    }
}

引用C# language specification(我添加了重点):

10.4.5.1 静态字段初始化

类的静态字段变量初始化器对应于 以文本顺序执行的任务序列 它们出现在类声明中。 如果是静态构造函数 (10.11节)存在于类中,执行静态字段 初始化器在执行该静态之前立即发生 构造函数。 否则,将执行静态字段初始值设定项 在第一次使用静态之前的依赖于实现的时间 该类的字段。

【讨论】:

  • 我找不到对象初始化顺序的可靠资源,但 both these 的帖子引用了强化这一点的相同顺序。
【解决方案2】:

来自MSDN

类的静态字段变量初始化器对应于 以文本顺序执行的任务序列 它们出现在类声明中。

所以尝试将初始化移动到 static 构造函数之前,或者将关联包含在 static 构造函数本身中。

尽管如此,您正在尝试一些不可能的事情,因为静态字段使用在静态构造函数中声明的变量。

试试这个:

private static AppSettingsSection _appSettingsLogsSection;
public static int LogSendIntervalMinutes;

static Configuration()
{
    var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
    _appSettingsLogsSection = config.GetSectionGroup("Logs").Sections["appSettings"] as AppSettingsSection;

    LogSendIntervalMinutes = Convert.ToInt32(_appSettingsLogsSection.Settings["LogSendIntervalMinutes"]);        }
}

【讨论】:

  • 现在我明白了,谢谢。尽管所有答案或多或少都解释了相同的事情,但我将其标记为答案,因为它是第一个:)
【解决方案3】:

使用ILSpyReflector查看编译代码你会找到答案。

实际上字段初始值设定项会在构造函数的实际代码之前自动移动到构造函数中。 (静态移动到静态,实例移动到实例构造器)。

使用反射器反编译你的类的代码:

实际的类结构是这样的:

您还可以看到编译器所做的优化,将构造函数中的两条单独的行集成到单行以初始化_appSettingsLogsSection。

【讨论】:

    【解决方案4】:

    静态字段的初始化实际上是类型构造函数的一部分,并在调用自定义类型构造函数之前执行。因此,如果LogSendIntervalMinutes 是一个实例字段,就没有问题,但由于它是静态的,它的初始化会在你的静态构造函数之前执行。您只需要将LogSendIntervalMinutes 的初始化放在您的类型构造函数中。

    【讨论】:

      猜你喜欢
      • 2014-07-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-07
      • 1970-01-01
      • 2015-06-28
      • 1970-01-01
      • 2011-05-08
      相关资源
      最近更新 更多