静态构造函数在静态字段之前未调用

时间:2014-02-02 10:56:09

标签: c# .net static clr

我有一个课程如下:

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。

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

4 个答案:

答案 0 :(得分:4)

在调用静态构造函数之前,始终初始化静态字段。您应该在静态构造函数中初始化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静态字段初始化

     

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

答案 1 :(得分:3)

来自MSDN

  

类的静态字段变量初始值设定项对应于a   以文本顺序执行的分配顺序   它们出现在课堂宣言中。

因此,请尝试将初始化移至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"]);        }
}

答案 2 :(得分:2)

使用ILSpyReflector查看编译代码,您将找到答案。

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

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

enter image description here

和实际的类结构如下所示:

enter image description here

此外,您可以看到编译器完成的优化,将构造函数中的两个单独行集成到单行中以初始化_appSettingsLogsSection。

答案 3 :(得分:1)

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