Как поймать или пометить потенциальные проблемы из-за порядка инициализации статического поля
Рассмотрим следующий код С#:
using System;
class Program
{
static string string1 = "AAA";
static string string2 = string1 + string3;
static string string3 = "BBB";
static void Main()
{
Console.WriteLine(string2);
}
}
Я написал код такой, как сегодня, и ожидал, что string2
будет содержать значение AAABBB
, но вместо этого он просто содержал AAA
. Я сделал некоторое чтение о порядке инициализации статических переменных, но мне кажется предпочтительнее, чтобы во время компиляции был сформирован некоторый тип предупреждения или ошибки.
Два вопроса:
-
Почему такой код разрешен для компиляции? (и если ответ таков: "потому что, как написано спецификацию С#", то почему это было написано именно так? Существуют ли причины, по которым я не понимаю, почему это не всегда всегда просто бросает ошибку времени компиляции?)
-
Есть ли способ получить предупреждение о компиляции или какой-либо другой флаг, если я в дальнейшем нечаянно напишу этот код в будущем?
Ответы
Ответ 1
Для вопроса 2:
Такие инструменты, как ReSharper, поймают эти вещи. В Visual Studio может быть настройка, которую вы можете включить для получения более подробного вывода компиляции.
![enter image description here]()
Здесь код после очистки Reshaper, который производит "AAABBB"
class Program
{
private const string String1 = "AAA";
private const string String2 = String1 + String3;
private const string String3 = "BBB";
static void Main()
{
Console.WriteLine(String2);
Console.ReadLine();
}
}
В качестве побочного примечания, я бы сказал, поскольку мы обычно читаем сверху вниз, кажется логичным, что инициализация происходит так же, как описано в 10.4.5.1 спецификации С#.
Ответ 2
Если вы думаете о своих статических инициализаторах как о логической части статического конструктора, все имеет смысл. В вашем случае это так, как если бы вы написали:
private static string String1;
private static string String2;
private static string String3;
static Program()
{
String1 = "AAA";
String2 = String1 + String3;
String3 = "BBB";
}
Причина, по которой статические инициализаторы не работают так, как вы хотите, состоит в невозможности в общем случае компилятора переупорядочить вещи. В этом случае и во многих других. Но если вы рассматриваете эффекты второго порядка, о которых я упоминал в своем ответе на аналогичный вопрос, компилятор не может надежно переупорядочить что-либо.
Было бы крайне сложно, если бы компилятор сделал это переупорядочивание "иногда".
Ответ 3
Что касается №1: причина, по которой код разрешен для компиляции, заключается в том, что в соответствии с 10.4.5.1 спецификации С# "инициализаторы статической переменной поля класса соответствуют последовательности назначений, которые выполняются в текстовый порядок, в котором они появляются в объявлении класса." Это означает, что ваша переменная string2
недвусмысленно инициализируется на "AAA" + null
. Возможно, вы должны хотя бы получить предупреждение... о том, почему ваша IDE решила не предупреждать вас, я не знаю.
Что касается №2: я не знаю. Это похоже на вопрос, который был бы более подходящим, если бы также был помечен вашей IDE.
Ответ 4
Это дизайн.
Инициализаторы статической переменной поля класса соответствуют последовательность присвоений, выполняемых в текстовом порядке в которые они отображаются в объявлении класса.
С# имеет политику definite assignment
, которая означает, что, например, поля инициализируются автоматически. Поэтому ваш string3
автоматически инициализируется на null
, и поэтому, что касается компилятора, оно уже имеет значение.
Это означает, что вывод string2
равен string1 + null
(это просто string2
), и поэтому нет никаких причин для компилятора делать какие-либо ошибки (хотя я вижу, как было бы полезно использовать это предупреждение).