Порядок статических конструкторов / инициализаторов в C # - PullRequest
19 голосов
/ 09 октября 2008

Во время работы над приложением C # я только что заметил, что в некоторых местах статические инициализаторы имеют зависимости друг от друга, например:

static private List<int> a = new List<int>() { 0 };
static private List<int> b = new List<int>() { a[0] };

Не делая ничего особенного, что сработало. Это просто удача? Есть ли в C # правила для решения этой проблемы?

Редактировать: (re: Panos) В файле лексический порядок кажется королем? что насчет файлов?

В поиске я пробовал циклическую зависимость, подобную этой:

static private List<int> a = new List<int>() { b[0] };
static private List<int> b = new List<int>() { a[0] };

и программа не работала одинаково (тестовый костюм не прошел через доску, и я не смотрел дальше).

Ответы [ 4 ]

16 голосов
/ 09 октября 2008

См. раздел 10.4 спецификации C # для правил здесь:

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

Другими словами, в вашем примере «b» инициализируется в состояние по умолчанию (ноль), и поэтому ссылка на него в инициализаторе «a» является допустимой, но приведет к исключению NullReferenceException.

Эти правила отличаются от правил Java (см. раздел 8.3.2.3 JLS для правил Java относительно прямых ссылок, которые являются более строгими). ​​

14 голосов
/ 09 октября 2008

Кажется, это зависит от последовательности строк. Этот код работает:

static private List<int> a = new List<int>() { 1 };
static private List<int> b = new List<int>() { a[0] };

пока этот код не работает (выдает NullReferenceException)

static private List<int> a = new List<int>() { b[0] };
static private List<int> b = new List<int>() { 1 };

Итак, очевидно, что никаких правил для циклической зависимости не существует. Однако странно, что компилятор не жалуется ...


РЕДАКТИРОВАТЬ - Что происходит "между файлами"? Если мы объявим эти два класса:

public class A {
    public static List<int> a = new List<int>() { B.b[0] };
}
public class B {
    public static List<int> b = new List<int>() { A.a[0] };
}

и попробуйте получить к ним доступ с помощью этого кода:

try { Console.WriteLine(B.b); } catch (Exception e) { Console.WriteLine(e.InnerException.Message.); }
try { Console.WriteLine(A.a); } catch (Exception e) { Console.WriteLine(e.InnerException.Message); }
try { Console.WriteLine(B.b); } catch (Exception e) { Console.WriteLine(e.InnerException.Message); }

мы получаем этот вывод:

The type initializer for 'A' threw an exception.
Object reference not set to an instance of an object.
The type initializer for 'A' threw an exception.

Таким образом, инициализация B вызывает исключение в статическом конструкторе A и поле lefts a со значением по умолчанию (null). Поскольку a равно null, b также не может быть правильно инициализировано.

Если у нас нет циклических зависимостей, все работает нормально.


РЕДАКТИРОВАТЬ: На всякий случай, если вы не читали комментарии, Джон Скит дает очень интересное чтение: Различия между статическими конструкторами и инициализаторами типов .

2 голосов
/ 09 октября 2008

Лично я бы избавился от статических инициализаторов, так как это не ясно, и добавил бы статический конструктор для инициализации этих переменных.

static private List<int> a;
static private List<int> b;

static SomeClass()
{
    a = new List<int>() { 0 };
    b = new List<int>() { a[0] };
}

Тогда вам не нужно гадать, что происходит, и вы ясно понимаете свои намерения.

0 голосов
/ 09 октября 2008

Да, тебе повезло. Появляется C # для выполнения кода в том порядке, в котором он появляется в классе.

static private List<int> a = new List<int>() { 0 };
static private List<int> b = new List<int>() { a[0] };

Будет работать, но ...

static private List<int> b = new List<int>() { a[0] };
static private List<int> a = new List<int>() { 0 };

Сбой.

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

static MyClass()
{
  a = new List<int>() { 0 };
  b = new List<int>() { a[0] };
}
...