public static int count = 5;
Они инициализируются в порядке их объявления в классе, сверху вниз.
Во вторую очередь, инициализируется статический конструктор. Если класс содержит такой конструктор:
static MyClass()
{
// ...
}
...то он выполняется после инициализации статических полей и перед созданием первого экземпляра класса.
Когда мы создаём класс, который содержит статические поля, важно инициализировать их в правильном порядке. Рассмотрим следующий код:
Console.WriteLine($"Value1: {Foo.value1}");
Console.WriteLine($"Value2: {Foo.value2}");
Console.WriteLine($"Bar: {Foo.bar}");
class Foo
{
public static int bar = value1 - value2;
public static int value1 = 8;
public static int value2 = 3;
}
На первый взгляд, код кажется правильным. Имеем максимальное значение 8, минимальное 3 и ожидаем, что поле bar будет равняться пяти. Однако, если запустить этот код для вывода значений, то увидим результаты, которые не были ожидаемы:
Value1: 8
Value2: 3
Bar: 0
Исправить это можно просто изменив порядок объявления полей:
class Foo
{
public static int value1 = 8;
public static int value2 = 3;
public static int bar = value1 - value2;
}
Теперь выполнение даёт правильные результаты:
Value1: 8
Value2: 3
Bar: 5
К сожалению, такой подход не создаёт идеального кода, читаемого и поддерживаемого. Вполне возможно, что позднее другой разработчик может изменить порядок полей, чтобы, например, отсортировать их в алфавитном порядке и невольно станет причиной ошибки. Лучше исправить это и установить значения в пределах статического конструктора, как показано ниже:
class Foo
{
static Foo()
{
value1 = 8;
value2 = 3;
bar = value1 - value2;
}
public static int value1;
public static int value2;
public static int bar;
}
Чтобы детально увидеть, в каком порядке происходит инициализация, запустим следующий код:
Console.WriteLine("1. Start");
Console.WriteLine($"4. Output: {Foo.a}");
class Foo
{
static Foo()
{
Console.WriteLine("3. Static ctor");
a = 10;
}
public static int a;
public static int b = SetB(); // Эквивалентно b = 3;
private static int SetB()
{
Console.WriteLine("2. Instance fields");
return 3;
}
}
Вывод:
1. Start
2. Instance fields
3. Static ctor
4. Output: 10
Вызываемое поле вообще равняется нулю (по дефолту для int), но, благодаря статическому конструктору, ему было присвоено значение в последнюю очередь до возврата управления в 4 пункт.
В таком случае, порядок уже не важен. Главное только не забыть присвоить значение необходимым полям.
Из примеров выше, думаю, вся картина наглядным образом теперь должна быть понятна.
А в связи с результатами последнего опроса, а также и с другим непредсказуемым поведением при проектировании, подобные посты я буду помечать новым хэштегом #НеочевидныеБаги. Он будет сообщать о тех случаях, где мы легко можем словить баг, а его обнаружение будет близко к нервному срыву.
Пусть подобные метки научат нас обращать внимание на те детали, которые кажутся незначительными.
#Полезно #Инициализация #НеочевидныеБаги