2014-02-02 3 views
3

У меня есть класс следующим образом:
Статический конструктор не вызывается перед статическими полями

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"]); 

    } 

Теперь, согласно моему пониманию, статический конструктор должен быть вызван до первой ссылки на любой статический элемент выполнен. Но удивительно, что это не так. Когда я ссылаюсь на LogSendIntervalMinutes из основного класса, вместо запуска статического конструктора вызов переходит прямо к статическому полю, в результате чего возникает исключение NullReferenceException.

Я делаю что-то неправильно здесь и правильно ли я понимаю?

+0

Посмотрите на код компиляции, используя 'ILSpy' или' Reflector', вы найдете свой ответ. Фактически инициализаторы полей автоматически перемещаются внутри конструктора до вашего фактического кода конструктора. (статические переходы к статическому и экземпляру к экземпляру contructor). –

+0

Я добавил моментальный снимок в ответ на ваш класс, используя отражатель для справки. –

ответ

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"]);  } 
} 
+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 Статическое поле инициализации

статическое поле инициализаторы переменных из класс соответствует последовательности назначений, которые выполняются в текстовом порядке в , которые они отображаются в объявлении класса. Если в классе существует статический конструктор (раздел 10.11), выполнение статического поля инициализаторы происходит непосредственно перед выполнением этого статического конструктора . В противном случае инициализаторы статического поля выполняются за время, зависящее от реализации, до первого использования статического поля этого класса.

+0

Я не могу найти надежный ресурс для порядка инициализации объекта, но [оба] (http: // www.csharp411.com/c-object-initialization/) [эти] (http://stackoverflow.com/questions/1920615/field-initialization) сообщения относятся к той же последовательности, что и подтверждает это. –

1

Инициализация статических полей на самом деле является частью конструктора типов и выполняется до вызова конструктора настраиваемого типа. Таким образом, если LogSendIntervalMinutes было полем экземпляра, проблема не возникла бы, но поскольку он является статическим, его инициализация выполняется перед вашим статическим конструктором. Вам просто нужно поставить инициализацию LogSendIntervalMinutes внутри вашего конструктора типов.

1

Посмотрите на код компиляции, используя ILSpy или Reflector, вы найдете ответ.

На самом деле поле Инициализаторы автоматически перемещаются внутри конструктора до вашего фактического кода конструктора. (статические переходы в статические и экземпляры на экземпляр contructor).

Decompile код вашего класса с помощью отражателя:

enter image description here

и фактическая структура класса выглядит следующим образом:

enter image description here

Также вы можете увидеть оптимизацию сделано компилятором для интеграции двух отдельные строки в конструкторе для одной строки для инициализации _appSettingsLogsSection.

Смежные вопросы